寫給所有程式設計師_回顧學習初期的五個為什麼
變數,函式(方法),類與物件,介面,抽象類
實際上,開始學習的java的時候,這些是基礎,也是疑惑。老師填鴨似的把這些知識塞給我,然後完全不知道為什麼要用,甚至剛剛學習完java還沒有做專案的時候,我對我的老師說,我雖然會用這些東西,但是完全不知道什麼時候要用,我為什麼要用他們。
現在,似乎有點明白了為什麼要用這些東西,於是我決定寫下來。
如果你是一位面向過程方面的程式設計師請看前兩個就夠了,第三個可近似理解成結構體,面向物件的程式設計師請看全部。如果你已經學習了一段時間,我認為你仍然有必要看看,因為我講的東西稍微有點特別,你應該沒有完全聽過。
1.為什麼要用變數
(1)並非所有的資料都是已知的。
比如你獲取電腦的寬度,比如你獲取一些網路資料,這些東西獲取之前,你不知道它是什麼,只知道型別或者大概的格式。偏偏你的程式中還要對他們進行處理,於是你需要使用變數,用它來儲存你獲取的未知事物,方便進行邏輯處理。
(2)變數有助於記憶。
變數型別幫助我們記憶變數大概的範圍,相當於量詞:一臺電腦的臺,一隻喜鵲的只。從臺字我們大概瞭解到這件東西有個底座,是個靜物;從隻字大概瞭解到應該是動物類的。
變數名幫助我們瞭解變數的用途,具體指的是什麼,就像生活中的所有事物,我們都會用個名字來代替他們方便交流,如電腦,手機,書包。。。
所以,變數型別要準確,變數名要清楚,這是對一個變數的基本要求。
如何做到變數型別要準確?
對於基本型別,在能不使用引用型別的情況下就不使用。對於一個整型數字比如5,我可以用int型別表示:
int num = 5;也可以用String型別表示String num = “5”;但相比較而言,整型int儲存數字更加合理,所以我們用整型。當然,像計算金額這種情況,使用BigDecimal當然會比double,float更加合理,所以我後面加了一句話在能不使用引用型別的情況下就不使用。
對於引用型別,型別名稱要儘量準確。假如你需要在網路請求後返回圖書詳細資訊,可以寫成BookInfo。但是通常我們在圖書列表中會返回圖書簡略資訊,所以這個名稱的識別率就變差,這時我們可以加個形容詞,變成:DetailBookInfo,外面的列表的簡略圖書資訊可用SimpleBookInfo。
注意,我這裡寫DetailBookInfo而非BookDetailInfo,因為看起來DetailBookInfo和SimpleBookInfo更容易區分,BookDetailInfo和BookSimpleInfo沒那麼容易區分。
如何做到變數名清楚?
首先,變數名的清楚是相對而言的。
簡單舉個例子,某小學有個同學叫張三丰,如果只有一個張三丰,老師叫他的時候只需要說“張三丰”,這位同學就知道叫的是他了。然後發現原來許可權有好幾個張三丰,而我們班只有一個張三丰,那麼老師如果叫的是我們班的張三丰,老師會說“X年X班張三丰”。然後某天,我們班又來了一個張三丰,這傢伙比較胖。所以老師叫他的時候,會說“X年X班胖三豐”,另一個叫“X年X班瘦三豐”,不同的變數名稱根據識別尺度的改變而發生改變。
第二,名字要一看就明白,不要不明覺厲。不要起a,b這種完全分不清是啥的變數名。前面舉得例子,老師叫張三丰,然後說“A,你出來一下。”張三丰知道叫的是他嗎?
如果我只要求1-100的和,這個和使用sum作為變數名就可以了。但如果我既需要求1-100的和也需要求1-1000的和,那麼單單用的sum1,sum2就不夠清楚了,因為單單從名稱看不出來誰是1-100的和。那麼可以這樣取名:
sumOfOne2Hundred,sumOfOne2Thousand,注意這裡我沒有用one2HundredSum和one2ThousandSum,因為那樣不容易識別。
2.為什麼要用函式(方法)
(1)分類查詢
比如我們要把大象塞進冰箱分成三步:第一步,開啟冰箱,第二步,把大象放進冰箱,第三步,把冰箱門關上。我把他寫成這樣:
public void fillElephantInRefrigerator(){
openRefrigerator();
putElephantInRefrigerator();
closeRefrigerator();
}
public void openRefrigerator(){
//此處略去100行
}
public void putElephantInRefrigerator(){
//此處略去100行
}
public void closeRefrigerator(){
//此處略去100行
}
假設每個步驟都需要寫100行程式碼,如果我比較關心putElephantInRefrigerator()這一步我做了什麼,因為我寫了函式,所以只需要看:openRefrigerator(),不,我不關心這個,然後看到putElephantInRefrigerator(),2行程式碼,就找到了正確的位置。如果我沒有用函式,看起來是這樣的:
public void fillElephantInRefrigerator(){
//此處略去100行
//此處略去100行
//此處略去100行
}
這樣我需要看到101行才能找到putElephantInRefrigerator()這部分我做了什麼,而且還不一定找的到。為什麼說不一定找的到?請看第二點。
(2)給步驟具體的含義
上面的例子中,如果不寫成具體的函式(方法),一堆程式碼很容易看的我們暈頭轉向,我們不會知道第一個100行是用於開啟冰箱的,因為我程式碼中並沒有提到這一點。但是,函式名能夠告訴我們。所以和變數名相同,不要起沒意義和沒區分度的名字。
(3)方便重複呼叫
比如我要把兩隻大象塞入冰箱,我只要加上這段程式碼:
fillElephantInRefrigerator();
fillElephantInRefrigerator();
如果我要把100只大象塞入冰箱,我只要這樣寫:
int elephantNum = 1;
for(int elephantNum = 1; elephantNum <= 100; elephantNum++){
fillElephantInRefrigerator();
}
有的時候,在不同的地方把大象塞入冰箱,那麼我們可以在每個需要的地方呼叫它。當我認為一部分程式碼可能經常使用的時候,我往往會把它寫成一個函式。
(4)特殊的一句話函式
有的時候,我們把一行程式碼單獨寫個函式,有一下幾種情況:
a.這是一個Bean或者其他物件,我需要寫getter,setter方法
public class Apple{
public int color;
public void setColor(){
this.color = color;
}
public int getColor(){
return color;
}
}
b.給邏輯判斷一定的含義
對於選擇結構的判斷語句,有時候判斷的內容不止一句,通過&&和||連線,有的時候他們太過複雜,一眼看不出在判斷什麼,這裡通過isGood()函式判斷了是否是良好。
int score = 82;
if(isGood(score)){
System.out.println("良好");
}
public boolean isGood(score){
return score >= 75 && score < 90;
}
c.返回特定值,使用三目運算子
public int getVisiblity(View v){
return v.getVisibility()==View.VISIBLE?1:0;
}
3.為什麼使用類與物件
舉個簡單的小例子:比如你是公司的老闆,公司剛剛起步,只有你一個人,於是你什麼都要做,因為公司前面的訂單並不多,所以你一個人忙的過來。然後,有一天,你一夜暴富,把公司開到和阿里巴巴一樣大,這個時候你需要每天處理上億訂單。
然後,你需要大量的招募人員,開始的時候每個人做一部分,這時候,你就需要權責劃分。權責劃分的過程,就是產生物件的過程。
簡單的說:物件產生的過程就是分工的過程。那麼,有哪些類呢?
(1)資料類,表示一種型別事物的特徵,純粹用於特徵描述,方便資料傳輸,資料返回(Bean)
public class Apple{
private double weight;
private String description;
//getter and setter
}
資料傳輸時,我們可以很方便的把它作為引數,傳到函式中處理,當有多個返回值時,可以通過資料類打包一起返回。
這種類的特點是,除了屬性,基本是getter和setter的方法。當然有的時候會有些特殊的,比如說我需要所有的特徵資訊,上述類可能加入以下程式碼:
public void printFeatures(){
System.out.println("重量是:"+weight+" 描述是"+description);
}
(2)同類事物管理
比如,我們要把Apple儲存在快取中,為了識別,加一個編號。
public class Apple{
private double weight;
private String description;
private int appleID;
//getter and setter
@Overriade
public boolean equals(Object o){
if(o == null)
return false;
if(o instance of Apple)
return appleID == ((Apple)o).getAppleID();
return false;
}
}
public class AppleCacheManager{
private List<Apple> cacheApples = new ArrayList<>();
public boolean hasApple(Apple apple){
return cacheApple.contains(apple);
}
public void addApple(Apple apple){
cacheApples.add(apple);
}
public void removeApple(Apple apple){
if(hasApple(apple))
cacheApples.remove(apple);
}
public void clearCache(){
cacheApples.clear();
}
}
(3)工具類
用於處理同一類事物,一般命名為XXUtil,把該類事物可能進行的處理函式歸納到一起,比如列印輸出類,這種類一般由靜態方法組成,也有物件類的,較少一些。
public class PrinterUtil{
public static void print(){
System.out.println("這句話用於測試Printer是否可用");
}
public static void print(int num){
System.out.println("這個數字是:"+num);
}
public static void print(double num){
System.out.println("這個小數是:"+num);
}
public static void print(String num){
System.out.println("這句話是:"+num);
}
}
(4)介面類
這個不列舉了,反正一放上去可以立刻能看到的就是介面。
(5)邏輯處理類,MVP的presenter或者MVC的Controller
這類是把ui的邏輯進行處理,對於沒用過設計模式的比較難描述,我還是用文字吧。如果介面有這樣一個功能:點選按鈕,把一些文字顯示到文字框,我把按鈕放在介面,把文字放到資料類中,獲取文字資訊這一步就通過presenter或者controller來處理。可能我要給這些文字加字首或者字尾,也在presenter或者controller中來處理。
(6)操作類,把對某種型別的操作封裝在單獨的類中,通常用於對同類物件的統一操作進行整合,新增一定的處理,或者是對於唯一物件的管理,避免出現多個物件(網路,資料庫等)
public class ListProduce{
private List mList = new ArrayList();
public void add(Object obj){
mList.add(obj);
System.out.println("添加了一條資料");
}
public void remove(){
mList.remove(obj);
System.out.println("刪除了一條資料");
}
public List copy(){
List tempList = new ArrayList();
tempList.addAll(mList);
return tempList;
}
}
大概就這麼多,其他的有關框架和設計模式的部分就不講了。前期需要用的基本就這幾種。簡而言之:類的存在的目的在於傳輸資料,處理資料,顯示介面,如果一段邏輯可以用於傳輸特定型別的資料,處理特定型別的資料,顯示某種特定的介面,那麼就可以把這段邏輯獨立成類。
為什麼要使用抽象類
抽象類常用於處理一堆同類事物共同要做的事。比如網頁,每個網頁都會有顯示載入等待過程,載入網址內容,顯示崩潰內容(404等),類似這種同類事物都要進行處理,並且可能需要相同處理過程的我們用抽象類:
public abstract class SimulationBrowser{
public void create(){
showLoadingDialog();
loadData();
}
public void showLoadingDialog(){
System.out.println("顯示載入動畫");
}
public void loadData(){
//進行網路非同步請求。。。這裡用個執行緒假裝一下,因為網路的篇幅太長。。。
new Thread(){
public void run(){
try{
Thread.sleep(5000);
printSuccessLog();
}catch(Exception e){
e.printStackTrace();
printErrLog();
}finally{
dismissDialog();
}
}
}.start();
}
public abstract void printSuccessLog();
public abstract void printErrLog();
public void dismissDialog(){
System.out.println("隱藏載入動畫");
}
}
上面的抽象類在子類呼叫create方法後正式工作,顯示顯示載入動畫,然後在載入之後進行網路請求(模擬),然後由子類重寫printSuccessLog(),和printErrLog()進行處理顯示錯誤資訊。可以看到,我們的抽象類將顯示載入動畫,進行網路請求這些步驟進行統一處理,然後對於部分不確定的工作使用抽象方法交給子類繼續完成,這就是抽象類需要做的。有經驗的程式設計師會在可能加抽象類的地方預先加抽象類,用於程式擴充套件。
簡單的說,抽象類就是統一這些類都要做的事,對於不確定的事交給繼承他的的類做。
為什麼要用介面
這是我學習初期最疑惑的問題,因為很多東西不用它也可以解決,為什麼要用介面呢?
(1)介面用在我們純粹知道要做哪些事情,但不知道具體內容的時候(較少用)。
假如每個需要工作的人都需要上班下班的考勤,假如你是清潔工早上5點以後到崗算遲到,假如你是工人,每天6點以後到崗是遲到,假如你是辦公室職員,每天9點以後到崗是遲到,因為考勤時都需要判斷是否遲到,所以制定統一介面:
public interface Attendance{
boolean isLate();
}
public class Cleaner implements Attendance{
public void printWork(){
System.out.println("打掃衛生");
}
public boolean isLate(){
Calendar mCalendar = Calendar.getInstance();
int hour = mCalendar.get(Calendar.HOUR_OF_DAY);
return hour > 5;
}
}
public class Worker implements Attendance{
public void printWork(){
System.out.println("搬磚");
}
public boolean isLate(){
Calendar mCalendar = Calendar.getInstance();
int hour = mCalendar.get(Calendar.HOUR_OF_DAY);
return hour > 6;
}
}
public class Officer implements Attendance{
public void printWork(){
System.out.println("打字");
}
public boolean isLate(){
Calendar mCalendar = Calendar.getInstance();
int hour = mCalendar.get(Calendar.HOUR_OF_DAY);
return hour > 9;
}
}
(2)使用介面來做監聽器或回撥(很頻繁)
大多時候介面是用這種,這種方式的好處是:靈活,可以返回多個數據。下列例子是個最簡單的回撥。
這裡我期望通過getDateTime獲取兩個返回值日期和時間,正常情況下只能返回一個,所以使用回撥。
public class CallbackTester(){
public static void main(String[] args){
CallbackProducer producer = new CallbackProducer();
producer.getDateTime(new DateTimeListener{
public void append(String date,String time){
System.out.println("日期是:"+date);
System.out.println("時間是:"+time);
}
});
}
}
public interface DateTimeListener{
void append(String date,String time);
}
public CallbackProducer{
public void appendAfterDate(DateTimeListener listener){
Calendar mCalendar = Calendar.getInstance();
Date nowDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
String date= dateFormat.format(nowDate);
String time= dateFormat.format(nowDate);
if(listener!=null)
listener.append(date,time);
}
}
另外一點,回撥能實現先處理後呼叫。
把上面的方式稍微改變一下:
public class CallbackTester(){
public static void main(String[] args){
CallbackProducer producer = new CallbackProducer();
producer.setOnDateTimeListener(new DateTimeListener{
public void append(String date,String time){
System.out.println("日期是:"+date);
System.out.println("時間是:"+time);
}
});
producer.getDateTime();
}
}
public interface DateTimeListener{
void append(String date,String time);
}
public CallbackProducer{
private DateTimeListener listener;
public void setOnDateTimeListener(DateTimeListener listener){
this.listener = listener;
}
public void appendAfterDate(){
Calendar mCalendar = Calendar.getInstance();
Date nowDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
String date= dateFormat.format(nowDate);
String time= dateFormat.format(nowDate);
if(listener!=null)
listener.append(date,time);
}
}
這裡我們把呼叫listener和初始化分開,在初始化過程中:
producer.setOnDateTimeListener(new DateTimeListener{
public void append(String date,String time){
System.out.println("日期是:"+date);
System.out.println("時間是:"+time);
}
});
這一部分可以在呼叫前先處理返回了這些值要做什麼。然後到這一步:
producer.getDateTime();
時才會呼叫介面,否則永遠都不會呼叫。這種把處理和呼叫分開的現象我們稱之為解耦。
總之,介面用於規定一類事物共同要做的事,可以間接實現了多返回值,能夠解耦。
總結:
1.為什麼使用變數?
變數能夠用於處理資料,好的變數名能夠便於記憶。
2.為什麼使用函式?
函式能夠將程式進行分割槽管理,方便查詢和修改,賦予步驟具體的含義。
3.為什麼使用類與物件?
我們需要對資料進行傳輸,處理,顯示,類與物件是媒介。
4.為什麼使用抽象類?
為了統一處理一類事物共同要做的事情的步驟順序,對於不能處理的具體事務,交給子類去處理。
5.為什麼使用介面?
介面能夠規定事務的共同需求(無法寫出具體的步驟,零散),實現多返回值,解耦。
之所以寫這篇文章是想提醒大家一個非,不問題。
例如:如果一個變數不能方便我們處理資料,變數名不能夠便於記憶,那麼就證明我們這個變數寫的不夠好。如果是第一條,或許要改變一下使用方式,讓變數更便捷易用;如果是第二條,那麼就要考慮一個合適的名字,讓變數更易讀。
如果一個東西不能滿足它的需求,那麼它就是殘次品,要麼刪除,要麼整改。希望讀者不要忘記這些本質的東西。