指南:從七大原則到重點模式代碼落地)
簡介這份《大話設計模式復習》文檔面向具備一定面向?qū)ο缶幊袒A、工作1至3年的研發(fā)人員系統(tǒng)梳理設計模式的核心知識體系。內(nèi)容從設計模式的定義、分類、特點與應用場景切入詳細講解單一職責、開放-封閉、依賴倒轉、里氏代換、迪米特法則、合成/聚合及接口隔離七大設計原則并深入剖析抽象工廠、策略、外觀、適配器、組合、單例、中介者七種常用模式以及其余十五種模式每種均配以定義、UML類圖、基礎代碼與實例。文檔還橫向?qū)Ρ炔煌J降牟町惪偨Y模式選擇的關鍵維度并附練習題與模擬試卷輔助鞏固。資源包為單個docx文件約3.66MB結構完整、目錄清晰便于按章節(jié)檢索學習。目前已有191人學習適合希望提升代碼可維護性、可擴展性與復用能力并在實際項目中靈活選用設計模式的開發(fā)者參考。1. 從一次代碼評審翻車說起為什么設計模式復習不能只背 23 個名字去年幫一個做企業(yè)級管理系統(tǒng)的團隊看代碼需求是加一種新的支付渠道。按理說這是個小改動結果開發(fā)同學在PaymentService里又塞了一個else if那個類已經(jīng)膨脹到 1800 多行改完之后回歸測試直接炸了三個老渠道。問題不在他不會寫代碼而在于他腦子里沒有「策略模式」這個可替換的抽象層——所有渠道邏輯硬編碼在一個類里擴展一次就動一次核心邏輯開放-封閉原則形同虛設。這就是設計模式復習這件事最尷尬的地方很多人能背出 23 個模式的名字和分類但真到項目里該用的時候想不起來或者用錯了地方。這份《大話設計模式復習》文檔的價值恰恰在于它不是干巴巴地列定義而是把七大設計原則、七大重點模式抽象工廠、策略、外觀、適配器、組合、單例、中介者和剩余 15 種模式全部配了 UML 類圖、基礎代碼和真實實例最后還做了易混淆模式的橫向?qū)Ρ群湍M試卷。它適合兩類人一是工作 1-3 年、想從「能寫功能」進階到「能設計結構」的開發(fā)者二是正在準備軟件工程方向課程設計或畢設答辯、需要一套完整模式應用綜述來支撐方案的同學。下面我按「需求怎么立 → 結構怎么設計 → 代碼怎么落地 → 怎么驗收」這條線把這份復習資料拆成一份可以直接照著走的方案。2. 需求與技術選型先想清楚復習設計模式到底要解決什么問題2.1 復習目標不是記住 23 個名字而是建立「問題→模式」的映射大多數(shù)人復習設計模式的失敗路徑是這樣的打開文檔從「單例模式」開始看看到「訪問者模式」已經(jīng)忘了前面一半合上文檔腦子里只?!负孟裼袀€工廠」。根本原因是把設計模式當成了知識點來背而不是當成工具箱來用。正確的復習目標應該反過來定先明確你手頭有哪些典型問題場景再倒推該用哪個模式。比如「對象創(chuàng)建邏輯復雜且需要解耦」對應工廠系列「算法需要運行時切換」對應策略模式「子系統(tǒng)接口太復雜需要簡化」對應外觀模式「不兼容接口需要轉換」對應適配器模式。這份文檔里七大重點模式之所以被單獨拎出來分析就是因為它們在真實項目中的出現(xiàn)頻率最高抽象工廠管對象族創(chuàng)建、策略管算法互換、外觀管接口簡化、適配器管接口轉換、組合管樹形結構、單例管全局唯一、中介者管多對象解耦——每一個都對應一類反復出現(xiàn)的工程問題。所以復習的第一件事是給自己列一張「我最近寫過的代碼里哪些地方改起來最痛苦」的清單然后帶著這些問題去對照模式。這比從頭背到尾效率高得多。2.2 技術棧與文檔結構Java 為主、C 補充UML 貫穿始終這份復習資料的技術選型很明確主體代碼用 Java 實現(xiàn)抽象工廠和策略模式的實例部分有 C 版本作為對照。這個選擇是合理的因為 Java 的接口和抽象類機制天然適合演示面向?qū)ο蟮脑O計模式而 C 的多態(tài)和虛函數(shù)則能幫你理解模式在更底層語言里的形態(tài)差異。文檔的整體結構分四層層次內(nèi)容作用基礎層設計模式定義、分類、特點、應用場景、UML 類圖規(guī)范建立統(tǒng)一術語和表達方式原則層七大設計原則SRP、OCP、DIP、LSP、LOD、CARP、ISP提供評判設計好壞的標準重點模式層抽象工廠、策略、外觀、適配器、組合、單例、中介者高頻模式深度剖析含完整代碼擴展與對比層剩余 15 種模式 易混淆模式對比 練習題與模擬試卷補齊知識面強化辨析能力這個結構的聰明之處在于先用原則建立判斷標準再用重點模式打透幾個典型場景最后用對比表把容易混淆的模式區(qū)分開。復習的時候如果時間緊可以按「原則 → 七大模式 → 對比表 → 模擬試卷」的順序走把剩余 15 種模式作為查閱手冊而不是精讀材料。2.3 適用人群與前置知識會寫類不等于會做設計這份資料明確說了適合「具備一定編程基礎、對面向?qū)ο笤O計有了解」的研發(fā)人員。翻譯一下就是你得能看懂 Java 的 interface、abstract class、繼承和多態(tài)知道什么是 UML 類圖中的泛化、實現(xiàn)、關聯(lián)、聚合、組合關系。如果這些還不熟直接看模式代碼會很吃力。具體來說前置知識包括三塊一是面向?qū)ο笕筇匦苑庋b、繼承、多態(tài)的實際運用經(jīng)驗不是概念背誦二是 UML 類圖的基本讀圖能力至少能分清實線空心三角是繼承、虛線空心三角是實現(xiàn)三是至少寫過 3000 行以上的項目代碼否則你很難體會到「這個類改起來真痛苦」是什么感覺也就無法理解模式要解決的真實痛點。提示如果你目前只寫過課程作業(yè)級別的小程序建議先找一個開源項目讀一讀它的分層結構再回來復習模式效果會好很多。3. 總體設計七大原則打底、三類模式分層、對比表收口的復習框架3.1 七大設計原則是判斷模式用得好不好的標尺文檔把七大原則放在模式分析之前這個順序不能顛倒。因為設計模式是「術」設計原則是「道」——你不知道什么是好的設計就沒法判斷一個模式用得對不對。七大原則里最核心的是三條開放-封閉原則OCP要求軟件實體對擴展開放、對修改關閉這是所有模式追求的終極目標依賴倒轉原則DIP要求高層模塊和低層模塊都依賴抽象這是實現(xiàn) OCP 的主要手段單一職責原則SRP要求一個類只有一個引起變化的原因這是控制類膨脹的第一道防線。剩下四條各有側重里氏代換原則LSP保證子類能替換父類而不破壞行為契約迪米特法則LOD要求對象之間盡量少直接通信、通過中間人轉發(fā)合成/聚合原則CARP優(yōu)先用組合而非繼承來降低耦合接口隔離原則ISP要求把大接口拆成小接口避免依賴不需要的方法。復習的時候我建議這樣用每看完一個模式回頭對照它「應用了哪些原則、違反了哪些原則」。比如文檔里抽象工廠模式的分析就明確寫了——它應用了依賴倒轉原則和合成/聚合原則但在新增產(chǎn)品等級結構時會違反開放-封閉原則。這種「有得有失」的分析才是真正理解模式的關鍵而不是簡單地說「這個模式好」。3.2 三類模式的分層邏輯創(chuàng)建型管怎么生、結構型管怎么拼、行為型管怎么動23 種設計模式按目的分為三大類這個分類不是隨便分的它對應了軟件設計中三個最基本的問題維度創(chuàng)建型模式5 種解決的是「對象怎么創(chuàng)建」的問題。單例模式保證全局唯一實例工廠方法模式把創(chuàng)建延遲到子類抽象工廠模式創(chuàng)建整個產(chǎn)品族建造者模式分步構建復雜對象原型模式通過克隆創(chuàng)建新對象。它們的共同目標是把對象的創(chuàng)建和使用分離讓客戶端不依賴具體類。結構型模式7 種解決的是「類和對象怎么組合」的問題。適配器模式轉換接口裝飾器模式動態(tài)添加職責代理模式控制訪問外觀模式簡化子系統(tǒng)接口橋接模式分離抽象與實現(xiàn)組合模式處理樹形結構享元模式共享細粒度對象。它們的共同目標是通過組合關系構建更靈活的結構。行為型模式10 種解決的是「對象之間怎么交互和分配職責」的問題。策略模式封裝可互換算法觀察者模式建立一對多依賴通知命令模式把請求封裝為對象狀態(tài)模式讓對象在內(nèi)部狀態(tài)改變時改變行為責任鏈模式沿鏈傳遞請求模板方法模式定義算法骨架迭代器模式順序訪問集合元素中介者模式集中管理對象間交互備忘錄模式捕獲和恢復狀態(tài)訪問者模式在不改變元素類的前提下定義新操作。復習時按這個分層走每類先抓 2-3 個最常用的模式吃透再擴展到同類其他模式比按字母順序或難度順序背要高效得多。3.3 對比表是復習收口的關鍵工具文檔最后一部分的對比表是我認為最有價值的內(nèi)容之一。它把容易混淆的模式成對或成組地放在一起比較簡單工廠 vs 工廠方法 vs 抽象工廠從「一個工廠類管所有產(chǎn)品」到「一個工廠接口管一類產(chǎn)品」再到「一個工廠接口管一族產(chǎn)品」抽象層級逐步提升適配器 vs 裝飾器 vs 代理三者都涉及「包裝」一個對象但適配器改變接口、裝飾器增強功能、代理控制訪問策略 vs 狀態(tài)兩者都通過組合持有可變行為但策略是客戶端主動選擇算法狀態(tài)是對象內(nèi)部狀態(tài)變化驅(qū)動行為切換觀察者 vs 中介者觀察者是一對多的廣播通知中介者是多對多的集中協(xié)調(diào)責任鏈 vs 中介者責任鏈是請求沿鏈傳遞直到被處理中介者是所有交互都經(jīng)過中心節(jié)點這些對比不是讓你背結論而是讓你在選型時能快速排除不合適的選項。比如你面對一個「需要動態(tài)切換算法」的場景看到「策略 vs 狀態(tài)」的對比就知道如果切換是客戶端主動發(fā)起的選策略如果是對象內(nèi)部狀態(tài)變化觸發(fā)的選狀態(tài)。4. 核心模塊實現(xiàn)從抽象工廠到中介者的代碼落地與設計說明4.1 抽象工廠模式可移植數(shù)據(jù)訪問層的完整實現(xiàn)抽象工廠是文檔里篇幅最長的模式分析因為它涉及的產(chǎn)品族概念最復雜。文檔給的實例是「可移植數(shù)據(jù)訪問程序」——同一套業(yè)務邏輯需要支持 SQL Server 和 Access 兩種數(shù)據(jù)庫每種數(shù)據(jù)庫都要創(chuàng)建 User 和 Department 兩類對象。先看抽象工廠接口和具體工廠的定義// 抽象工廠接口聲明創(chuàng)建產(chǎn)品族的方法 public interface IFactory { IUser CreateUser(); // 創(chuàng)建 User 對象的接口 IDepartment CreateDepartment(); // 創(chuàng)建 Department 對象的接口 } // SQL Server 具體工廠創(chuàng)建 SQL Server 產(chǎn)品族 public class SqlServerFactory implements IFactory { public IUser CreateUser() { return new SqlserverUser(); // 返回 SQL Server 的 User 實現(xiàn) } public IDepartment CreateDepartment() { return new SqlserverDepartment(); } } // Access 具體工廠創(chuàng)建 Access 產(chǎn)品族 public class AccessFactory implements IFactory { public IUser CreateUser() { return new AccessUser(); } public IDepartment CreateDepartment() { return new AccessDepartment(); } }這段代碼的關鍵設計點在于客戶端只依賴IFactory、IUser、IDepartment三個抽象完全不感知具體用的是哪個數(shù)據(jù)庫。切換數(shù)據(jù)庫時只需要換一個工廠實例業(yè)務代碼一行不用改。// 客戶端代碼只依賴抽象接口 public class Client { public static void main(String[] args) { // 切換數(shù)據(jù)庫只需改這一行 IFactory factory new SqlServerFactory(); // IFactory factory new AccessFactory(); IUser iu factory.CreateUser(); iu.Insert(new User()); iu.GetUser(1); IDepartment id factory.CreateDepartment(); id.Insert(new Department()); id.GetDepartment(1); } }參數(shù)說明IFactory是抽象工廠接口SqlServerFactory和AccessFactory是具體工廠IUser和IDepartment是抽象產(chǎn)品接口SqlserverUser、AccessUser、SqlserverDepartment、AccessDepartment是具體產(chǎn)品??蛻舳送ㄟ^工廠接口獲取產(chǎn)品通過產(chǎn)品接口調(diào)用業(yè)務方法。這個模式的優(yōu)缺點文檔寫得很清楚優(yōu)點是分離接口和實現(xiàn)、客戶端與具體產(chǎn)品解耦、增加產(chǎn)品族時符合依賴倒轉原則缺點是增加新的產(chǎn)品等級結構比如再加一個IScore接口時需要修改抽象工廠和所有具體工廠違反開放-封閉原則。所以抽象工廠適合「產(chǎn)品族穩(wěn)定、產(chǎn)品等級結構可能擴展」的場景反過來就要慎重。4.2 策略模式商場收銀系統(tǒng)的算法互換設計策略模式解決的是「算法需要運行時切換」的問題。文檔用的實例是商場收銀系統(tǒng)不同折扣策略對應不同的收費算法。// 策略接口聲明算法方法 interface Strategy { void doAlgorithm(); } // 具體策略 A class StrategyA implements Strategy { public void doAlgorithm() { // 算法 A 的具體實現(xiàn) } } // 上下文持有策略對象委托執(zhí)行 class Context { private Strategy strategy; // 運行時注入具體策略 public void setStrategy(Strategy s) { strategy s; } public void executeStrategy() { strategy.doAlgorithm(); // 委托給具體策略執(zhí)行 } }設計說明Context類是整個模式的核心它持有一個Strategy接口的引用但不關心具體是哪個實現(xiàn)。客戶端通過setStrategy()方法在運行時注入不同的策略對象然后統(tǒng)一調(diào)用executeStrategy()。這樣新增一種折扣算法只需要實現(xiàn)一個新的Strategy子類不需要修改Context和已有策略類完全符合開放-封閉原則。文檔還給了 C 版本的收銀系統(tǒng)實現(xiàn)核心思路一致只是用虛函數(shù)替代了 Java 的接口。對比看兩種語言的實現(xiàn)能幫你理解策略模式的本質(zhì)不管語言怎么變「定義算法族 → 封裝每個算法 → 使它們可互換」這個結構是不變的。策略模式應用的設計原則包括開放-封閉原則新增策略不改已有代碼、依賴倒轉原則Context 依賴 Strategy 抽象而非具體策略、合成/聚合原則Context 通過組合持有 Strategy 而非繼承。它和狀態(tài)模式的區(qū)別在于策略模式的切換由客戶端主動發(fā)起狀態(tài)模式的切換由對象內(nèi)部狀態(tài)變化驅(qū)動。4.3 單例模式與中介者模式全局唯一與多對多解耦單例模式是文檔里篇幅較長的另一個模式因為它的實現(xiàn)方式有多種餓漢式、懶漢式、雙重檢查鎖、靜態(tài)內(nèi)部類、枚舉每種都有線程安全和性能上的取舍。核心目標只有一個保證一個類只有一個實例并提供一個全局訪問點。// 雙重檢查鎖實現(xiàn)兼顧線程安全和延遲加載 public class Singleton { // volatile 禁止指令重排序防止拿到未初始化完的對象 private static volatile Singleton instance; private Singleton() {} // 私有構造禁止外部 new public static Singleton getInstance() { if (instance null) { // 第一次檢查避免不必要的同步 synchronized (Singleton.class) { if (instance null) { // 第二次檢查確保只創(chuàng)建一次 instance new Singleton(); } } } return instance; } }參數(shù)說明volatile關鍵字是必須的它禁止 JVM 指令重排序防止在instance new Singleton()這行代碼中對象還沒初始化完成就把引用賦值給instance導致其他線程拿到一個半成品對象。這是單例模式最容易翻車的地方也是面試高頻考點。中介者模式解決的是「多個對象之間交互復雜、耦合嚴重」的問題。文檔里的定義是用一個中介對象來封裝一系列的對象交互使各對象不需要顯式地相互引用。同事類之間不直接通信全部通過中介者轉發(fā)。// 抽象中介者 abstract class Mediator { public abstract void send(String message, Colleague colleague); } // 抽象同事類 abstract class Colleague { protected Mediator mediator; public Colleague(Mediator mediator) { this.mediator mediator; } } // 具體同事類 A class ConcreteColleagueA extends Colleague { public ConcreteColleagueA(Mediator mediator) { super(mediator); } public void send(String message) { mediator.send(message, this); // 通過中介者轉發(fā) } public void notify(String message) { // 接收來自中介者的通知 } }設計說明中介者模式把多對多的網(wǎng)狀依賴變成了一對多的星形依賴同事類之間不再直接引用所有交互都經(jīng)過中介者。這符合迪米特法則——兩個類若不必直接通信就不發(fā)生直接作用。代價是中介者本身可能變得很復雜如果同事類太多、交互邏輯太雜中介者會膨脹成一個「上帝類」。所以中介者模式適合交互規(guī)則明確且相對穩(wěn)定的場景不適合交互邏輯頻繁變化的場景。5. 避坑與常見問題復習設計模式最容易翻車的五個點5.1 把模式當模板硬套為了用而用現(xiàn)象項目里明明只有一個數(shù)據(jù)庫、一種支付方式卻非要上抽象工廠和策略模式代碼量翻了三倍維護的人一臉懵。原因把「用了多少模式」當成了設計能力的體現(xiàn)忽略了模式是用來解決特定問題的工具不是目的。解決每次想引入一個模式之前先問自己三個問題——不用這個模式會怎樣未來真的會有多種實現(xiàn)嗎引入后代碼是更簡單還是更復雜了如果答案是「不用也能跑」「短期內(nèi)不會擴展」「引入后更復雜」那就先別用。模式是等到痛點出現(xiàn)時才上的不是提前鋪路的。5.2 混淆策略模式和狀態(tài)模式現(xiàn)象寫了一個策略模式但策略的切換邏輯寫在 Context 內(nèi)部根據(jù)某個字段值自動切換結果和狀態(tài)模式長得一模一樣自己都說不清用的是哪個。原因兩個模式的類圖結構幾乎相同都是 Context 持有抽象接口接口有多個實現(xiàn)區(qū)別只在「誰驅(qū)動切換」和「切換的語義」。解決記住一條判斷標準——如果切換是客戶端主動選擇的比如用戶選了「打八折」用策略模式如果切換是對象內(nèi)部狀態(tài)變化自動觸發(fā)的比如訂單從「待支付」變成「已支付」后行為自動改變用狀態(tài)模式。策略模式的 Context 通常提供setStrategy()方法供外部調(diào)用狀態(tài)模式的 Context 通常內(nèi)部管理狀態(tài)轉換。5.3 單例模式忘記處理線程安全現(xiàn)象本地測試一切正常上線后偶爾出現(xiàn)兩個單例實例導致配置不一致或資源重復初始化。原因用了懶漢式但沒有加鎖或者加了鎖但沒加volatile多線程環(huán)境下指令重排序?qū)е履玫轿闯跏蓟甑膶ο?。解決如果對延遲加載沒有硬性要求直接用餓漢式類加載時就創(chuàng)建實例JVM 保證線程安全如果需要延遲加載用雙重檢查鎖加volatile或者用靜態(tài)內(nèi)部類方式利用 JVM 的類加載機制保證線程安全且實現(xiàn)延遲加載。最省事的是枚舉單例天然線程安全且防止反射攻擊。5.4 抽象工廠新增產(chǎn)品等級結構時大面積改代碼現(xiàn)象原本只支持 User 和 Department 兩類對象的數(shù)據(jù)庫訪問層現(xiàn)在要加一個 Score 對象結果抽象工廠接口、兩個具體工廠類、所有相關代碼都要改。原因抽象工廠模式的設計目標是「增加產(chǎn)品族容易增加產(chǎn)品等級結構困難」。產(chǎn)品族是橫向的SQL Server 族、Access 族產(chǎn)品等級結構是縱向的User、Department、Score??v向擴展必然要改工廠接口。解決如果產(chǎn)品等級結構經(jīng)常變化抽象工廠不是好選擇可以考慮用簡單工廠配合反射或者改用工廠方法模式。如果產(chǎn)品等級結構穩(wěn)定、產(chǎn)品族可能擴展抽象工廠才是合適的。選型前先判斷哪個維度更可能變化。5.5 外觀模式把不該簡化的接口也簡化了現(xiàn)象為了「讓調(diào)用方便」寫了一個外觀類把子系統(tǒng)所有接口都包了一層結果子系統(tǒng)新增功能時外觀類也要跟著改而且有些調(diào)用方需要精細控制子系統(tǒng)外觀類反而成了障礙。原因外觀模式的定位是「為子系統(tǒng)提供一個簡化的統(tǒng)一接口」它不應該屏蔽子系統(tǒng)的所有能力只應該封裝那些高頻、復雜的組合調(diào)用。解決外觀類只封裝最常用的幾組操作不要試圖覆蓋子系統(tǒng)的全部功能。需要精細控制的地方允許調(diào)用方直接訪問子系統(tǒng)。外觀模式的價值在于降低客戶端與子系統(tǒng)的耦合不是完全隔離。6. 驗收與答辯用模擬試卷和對比表做一輪自檢復習到最后怎么判斷自己真的掌握了而不是「看過了」這份文檔給了一個很實用的收口方式練習題加模擬試卷。我的建議是把它當成驗收環(huán)節(jié)來用而不是當額外的負擔。具體操作分三步走。第一步合上文檔拿一張白紙把七大設計原則和 23 種模式按創(chuàng)建型、結構型、行為型三類默寫出來每個模式用一句話寫清楚「它解決什么問題」。寫不出來的就是沒記住的回去補。第二步打開文檔最后的對比表遮住結論部分自己先判斷每組模式的區(qū)別再對照文檔看說得對不對。比如「適配器 vs 裝飾器 vs 代理」這組你能不能準確說出適配器改變接口、裝飾器增強功能、代理控制訪問說不清楚就回去看對應章節(jié)。第三步做模擬試卷重點看錯題涉及的是哪個模式、哪個原則針對性補漏。答辯場景下有幾個高頻問題值得提前準備。一是「你這個項目里用了哪些設計模式為什么選它不選別的」——回答時一定要結合具體場景說清楚不用這個模式會有什么問題。二是「抽象工廠和工廠方法有什么區(qū)別」——抓住「一個產(chǎn)品族 vs 一個產(chǎn)品等級結構」這個核心差異。三是「策略模式和狀態(tài)模式怎么區(qū)分」——用「客戶端主動切換 vs 內(nèi)部狀態(tài)驅(qū)動切換」來回答。四是「單例模式有哪些實現(xiàn)方式各自優(yōu)缺點是什么」——至少能說出餓漢式、雙重檢查鎖、靜態(tài)內(nèi)部類三種。我自己的習慣是每次復習完一個模式就找一個自己寫過的項目代碼看看能不能用這個模式重構一小塊。能重構成功說明真懂了重構完發(fā)現(xiàn)更復雜了說明這個場景不適合用這個模式也是一種收獲。從那以后我每次準備設計模式相關的答辯或評審都強制走一遍「默寫 → 對比 → 重構」這三步比反復看文檔有效得多。希望這份拆解能幫你把設計模式復習從「背了忘、忘了背」的循環(huán)里拉出來真正變成一套能用在項目和答辯里的方案。完整文檔里還有剩余 15 種模式的詳細分析和更多實例代碼需要的話可以拿去對照著過一遍。本文還有配套的精品資源點擊獲取