背景:聽說設計模式是進入BAT的必經之路。
First、何謂設計模式:
設計模式(Design Pattern)是一套被反復使用、多數人知曉的、經過分類的、代碼設計經驗的總結。
設計模式的好處&學習目的:
1、為了代碼可重用行、讓代碼更易被他人理解、保證代碼的可靠性、使代碼編寫真正實現工程化;
2、設計模式便於我們維護項目,增強系統的健壯性和可擴展性;
3、設計模式還可以鍛煉碼農的設計思維、升華代碼質量等。
六大指導原則:
程序設計模式有六大基本指導原則,但規則畢竟都是人定的,So我們要靈活遵守、靈活運用這六大原則。
一、開-閉原則:
1、開閉原則顧名思義就是對修改關閉,對擴展開放;
2、開閉原則是六大原則的核心,即我們之后做的任何改變都不需要修改原有的代碼,只需要加入一些新的實現即可達到目的;
3、開閉原則也是任何一個系統設計期望達到的理想境界。
二、單一職責原則:
單一職責即每個類都只負責單一的功能,莫要太貪心,除此之外盡量把一個類的功能完善到極致,可以用final來修飾的程度。下面就有一個例子類Scientific 從一個文件中讀取兩個數,並返回兩者之和,如此設計可以很明顯的看到它們之間的職責問題存在太多耦合了。如圖:
上圖中顯示的Scientific 類並沒有錯,但是我們要是讀取文件的地址改變了呢?要是結果改為兩者之差、兩者之積或兩者之商或者是兩者之模呢?然后,我們需要復制許多份相同的代碼,想想這種設計就不合理;這時我們就要考慮下“單一職責原則”,分離出一個類ReadFile 來讀取數據,再分離出一個類DesignScientific 對讀取到的數據進行處理(加減乘除等)。
1 import java.io.BufferedReader; 2 import java.io.FileReader; 3 public class ReadFile { 4
5 private int numOne; 6 private int numTwo; 7
8 public ReadFile(String path) throws Exception { 9 BufferedReader br = new BufferedReader(new FileReader(path)); 10 numOne = Integer.valueOf(br.readLine()); 11 numTwo = Integer.valueOf(br.readLine()); 12 System.out.println("numOne is: "+numOne+" && "+"numTwo is: "+numTwo); 13 } 14
15 public int getNumOne() { 16 return numOne; 17 } 18
19 public int getnumTwo() { 20 return numTwo; 21 } 22 }
如此,將一個類拆為兩個,既不會有那么多重復的代碼,也多了跟多結果的選擇性,也恰恰體現了單一職責原則是我們設計模式最應該遵守的原則之一。
三、里氏替換原則:
里氏替換原則具體指的是一個子類可以在替換掉其父類后正常工作,也就是自類不應該重寫父類的方法,其主要作用是規范繼承是子類的一些書寫規則,主要目的是保持父類方法不被覆蓋。
1、子類可以實現父類的抽象方法,但不能覆蓋父類的抽象方法(要不重新寫給類算了,干嘛要繼承);
2、子類中可以增加自己特有的方法;
3、當子類覆蓋或實現父類方法時,方法的前置條件(方法的形參)要比其父類方法的輸入參數更寬松(如:fun(ArrayLIst, list) VS fun(List, list) );
4、當子類的方法實現父類的抽象方法時,方法的后置條件(方法的返回值)要比其父類更嚴格。
四、接口隔離原則:
接口隔離原則也叫做接口最小化原則,指的是一個接口擁有的行為應該盡可能的最小。
1、如果設計的時候沒有考慮到接口隔離原則,就會出現一個類實現了一個接口但實現類中只有個別的方法實現了其他方法都是空的狀況,導致強制實現了不得不實現而又本不該實現的方法,而最終也一直沒有調用過此方法,造成資源浪費;
2、比如我們設計一個手機Mobile的接口時,就要考慮手機哪些屬性時必須的,要讓該接口盡量最小最細化,即只要是手機就必須要具備的屬性。
沒有遵守接口隔離原則的Mobile接口設計:
1 public interface Mobile {
2 public void call(); //手機可以打電話
3 public void sendMessage(); //手機可以發短信
4 public void weChat(); //手機可以上微信weChat?
5 }
上述Mobile接口很明顯不是一個手機必須具備的功能屬性,那么什么的Mobile接口就不是最小接口,因為非智能手機就只可以打電話和發短信,而上微信就是智能手機的專屬特性,如此就會有多余的實現方法,可以做如下修改,充分考慮接口隔離原則。
1 public interface SmartPhone extends Mobile{
2 public void weChat(); //智能手機的接口就可以加入這個方法了
3 }
五、依賴倒置原則:
這個原則描述的是高層模塊不該依賴於低層模塊,兩個模塊都應該依賴依賴於抽象,抽象不應該依賴於細節,細節應該依賴於抽象。因為實現都是易變的,只有抽象是穩定的,所以當我們依賴於抽象是,實現的變化就不會影響客戶端的調用;就比如上面“單一職責”中的計算器的例子,計算器其實是依賴於數據讀取類的,這樣設計還是有一些缺陷,因為若是當數據不是在文件里,而是在數據庫當中呢?這時候為了不影響現有的代碼,只能將ReaderFile類整個大改,或是新增一個DBReader類,把程序代碼中所有使用到ReaderFile讀取到地方替換成DBReader,這樣做勉強可以接受,但是仍然達不到可復制化的標准;若是數據讀取有的是從數據庫、有點是從XML文件、有的是從網絡或是從鍵盤輸入讀取,這時就有充分考慮依賴倒置原則。
1 public interface Reader {
2 //依賴倒置抽象出的一個抽象接口
3 public int getA(); 4 public int getB(); 5 }
依賴倒置原則讓我們抽象出一個抽象類或者接口,來表述數據讀取行為,然后讓上面所有的讀取方式所實現的類都實現該接口,客戶端方面只使用我們定義好的接口,當我們的實現變化時,我們只需設置下不同的實際類型就OK了,這樣設計對於系統的擴展行將會是一個巨大的提升;這樣設計的話,計算器就依賴於一個很穩定的抽象接口,之后不論是從哪里讀取數據,兩個獲取數據的方法都不會改變;無論是DBReader、XMLReader、NetReader或是OutPutStreamReader之類的都可以實現Reader這個抽象接口,不管是從哪里讀取數據都OK,因為我們不需要關心這個,只要可以從Reader接口中獲得A和B的值就OK了;同時,依賴於抽象也體現了JAVA語言的動態特性。
六、迪米特原則:
迪米特原則也稱最小知道原則,就是一個類盡量不應該知道(包括)其他類太多的東西,不要和其他類有太多的交集。該原則的制定的終極目的就是解耦,即將細節全部高內聚於類的內部,其他的類只需知道這個類主要提供的功能就OK了,減少不必要的依賴,高聚合低耦合。