1. 程式人生 > >寫給所有程式設計師_回顧學習初期的五個為什麼

寫給所有程式設計師_回顧學習初期的五個為什麼

變數,函式(方法),類與物件,介面,抽象類

實際上,開始學習的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.為什麼使用介面?
介面能夠規定事務的共同需求(無法寫出具體的步驟,零散),實現多返回值,解耦。

之所以寫這篇文章是想提醒大家一個非,不問題。
例如:如果一個變數不能方便我們處理資料,變數名不能夠便於記憶,那麼就證明我們這個變數寫的不夠好。如果是第一條,或許要改變一下使用方式,讓變數更便捷易用;如果是第二條,那麼就要考慮一個合適的名字,讓變數更易讀。

如果一個東西不能滿足它的需求,那麼它就是殘次品,要麼刪除,要麼整改。希望讀者不要忘記這些本質的東西。