初探設計模式六大原則
- 2019 年 10 月 31 日
- 筆記
前言
目錄
-
單一職責原則
-
里氏替換原則
-
依賴倒置原則
-
介面隔離原則
-
迪米特原則
-
開閉原則
P1.單一職責原則(Single Responsibility Principle)
public class People { public void playZhiHu () { System.out.println("玩知乎"); } public void doSports () { System.out.println("打乒乓球"); } public void work () { System.out.println("工作"); } }
-
玩知乎,這是知乎er的職責
-
打乒乓球,這是業餘運動愛好者的職責
-
工作,這是“普普通通上班族”的職責(似乎暴露了什麼)
// 知乎er public interface ZhiHuer { public void playZhiHu(); } // 上班族 public interface OfficeWorkers { public void work(); } // 業餘運動愛好者 public interface AmateurPlayer { public void doSports(); }
public class People implements ZhiHuer,AmateurPlayer,OfficeWorkers{ public void playZhiHu () { System.out.println("玩知乎"); } public void doSports () { System.out.println("打乒乓球"); } public void work () { System.out.println("工作"); } }
public class Index { public static void main (String args []) { People people = new People(); ZhiHuer zhiHuer = new People(); zhiHuer.playZhiHu(); // 輸出:玩知乎 OfficeWorkers workers = new People(); workers.work(); // 輸出: 工作 AmateurPlayer players = new People(); players.doSports(); // 輸出:打乒乓球 } }
P2.里氏替換原則(liskov substitution principle)
public abstract class Father { // 認真工作 public abstract void work(); // 其他方法 }
子類
public class Son extends Father { @Override public void work() { // 我實現了爸爸的work方法,旦我什麼也不做! } }
子類雖然表面上實現了父類的方法,但是他實際上並沒有實現父類要求的邏輯。里氏替換原則要求我們避免這種“塑料父子情”,如果出現子類不得不脫離父類方法範圍的情況, 採取其他方式處理,詳情參考《設計模式之禪》
P3.依賴倒置原則 (dependence inversion principle)
-
高層的模組不應該依賴於低層的模組,這兩者都應該依賴於其抽象
-
抽象不應該依賴細節
-
細節應該依賴抽象
// 底層模組1:開發者 public class Coder { public void develop (Linux linux) { System.out.printf("開發者正在%s系統上進行開發%n",linux.getSystemName()); } } // 底層模組2:Linux作業系統 public class Linux { public String name; public Linux(String name){ this.name = name; } public String getSystemName () { return this.name; } } // 高層模組 public class Index { public static void main (String args []) { Coder coder = new Coder(); Linux ubuntu = new Linux("ubuntu系統"); // ubuntu是一種linux作業系統 coder.develop(ubuntu); } }
開發者正在ubuntu系統系統上進行開發
但是我們能發現其中的問題:
// 程式設計師介面 public interface Programmer { public void develop (OperatingSystem OS); } // 作業系統介面 public interface OperatingSystem { public String getSystemName (); } // 低層模組:Linux作業系統 public class Linux implements OperatingSystem{ public String name; public Linux (String name) { this.name = name; } @Override public String getSystemName() { return this.name; } } // 低層模組:Window作業系統 public class Window implements OperatingSystem { String name; public Window (String name) { this.name = name; } @Override public String getSystemName() { return this.name; } } // 低層模組:開發者 public class Coder implements Programmer{ @Override public void develop(OperatingSystem OS) { System.out.printf("開發者正在%s系統上進行開發%n",OS.getSystemName()); } } // 高層模組:測試用 public class Index { public static void main (String args []) { Programmer coder = new Coder(); OperatingSystem ubuntu = new Linux("ubuntu系統"); // ubuntu是一種linux作業系統 OperatingSystem windows10 = new Window("windows10系統"); // windows10 coder.develop(ubuntu); coder.develop(windows10); } }
P4. 介面隔離原則(interface segregation principle)
-
介面要足夠細化,當然了,這會讓介面的數量變多,但是每個介面會具有更加明確的功能
-
在1的前提下,類應該依賴於“最小”的介面上
public interface ZhiHuer { // 認真撰文 public void seriouslyWrite(); // 友好評論 public void friendlyComment(); // 無腦抬杠 public void argue(); // 鍵盤攻擊 public void keyboardAttack (); }
public interface PositiveZhiHuer { // 認真撰文 public void seriouslyWrite(); // 友好評論 public void friendlyComment(); } public interface NegativeZhihuer { // 無腦抬杠 public void argue(); // 鍵盤攻擊 public void keyboardAttack (); }
-
單一職責原則和介面隔離原則雖然看起來有點像,好像都是拆分,但是其實側重點是不一樣的,“職責”的粒度其實是比“隔離介面”的粒度要大的
-
基於1中闡述的原因,其實 單一職責原則 和 介面隔離原則是可能會產生衝突的,因為介面隔離原則要求粒度儘可能要細,但是單一職責原則卻不同,它要求拆分既不能過粗,但也不能過細,如果把原本單一職責的介面分成了“兩個0.5職責的介面”,那麼這就是單一職責所不能允許的了。
-
當兩者衝突時,優先遵循 單一職責原則
P5.迪米特原則 (law of demeter)
// 我的直接朋友 public class MyFriend { // 找他的朋友 public void findHisFriend (FriendOfMyFriend fof) { System.out.println("這是朋友的朋友:"+ fof.name); } } // 朋友的朋友,但不是我的朋友 public class FriendOfMyFriend { public String name; public FriendOfMyFriend(String name) { this.name = name; } } // 我 public class Me { public void findFriend (MyFriend myFriend) { System.out.println("我找我朋友"); // 注意這段程式碼 FriendOfMyFriend fmf = new FriendOfMyFriend("陌生人"); myFriend.findHisFriend(fmf); }; }
-
一個類只和朋友類交流,朋友類指的是出現在成員變數、方法的輸入輸出參數中的類
-
一個類不和陌生類交流,即沒有出現在成員變數、方法的輸入輸出參數中的類
// 我朋友 public class MyFriend { public void findHisFriend () { FriendOfMyFriend fmf = new FriendOfMyFriend("陌生人"); System.out.println("這是朋友的朋友:"+ fmf.name); } } // 朋友的朋友,但不是我的朋友 public class FriendOfMyFriend { public String name; public FriendOfMyFriend(String name) { this.name = name; } } // 我 public class Me { public void findFriend (MyFriend myFriend) { System.out.println("我找我朋友"); myFriend.findHisFriend(); }; }
P6. 開閉原則(open closed principle)
總結
-
原則不是死板的而是靈活的
-
一些原則其實是存在一定的衝突的,重要的是權衡,是掌握好度
-
六大原則是23種設計模式的靈魂,六大原則指導了設計模式,設計模式體現了六大原則