一、設計模式的來源
設計模式(Design Pattern)是前輩們對代碼開發經驗的總結,是解決特定問題的一系列套路。它不是語法規定,而是一套用來提高代碼可複用性、可維護性、可讀性、穩健性以及安全性的解決方案。
1995 年,GoF(Gang of Four,四人組/四人幫)合作出版了《設計模式:可複用面向對象軟件的基礎》一書,共收錄了 23 種設計模式,從此樹立了軟件設計模式領域的里程碑,人稱「GoF 設計模式」。
二、設計模式的六大原則(SOLID)
總原則——開閉原則(Open Closed Principle)
一個軟件實體,如類、模塊和函數應該對擴展開放,對修改關閉。
在程序需要進行拓展的時候,不能去修改原有的代碼,而是要擴展原有代碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好,易於維護和升級。
想要達到這樣的效果,我們需要使用接口和抽象類等。
1、單一職責原則(Single Responsibility Principle)
一個類應該只有一個發生變化的原因。
不要存在多於一個導致類變更的原因,也就是説每個類應該實現單一的職責,否則就應該把類拆分。
2、里氏替換原則(Liskov Substitution Principle)
所有引用基類的地方必須能透明地使用其子類的對象。
任何基類可以出現的地方,子類一定可以出現。里氏替換原則是繼承複用的基石,只有當衍生類可以替換基類,軟件單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。
里氏代換原則是對“開-閉”原則的補充。實現“開閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏替換原則是對實現抽象化的具體步驟的規範。里氏替換原則中,子類對父類的方法儘量不要重寫和重載。因為父類代表了定義好的結構,通過這個規範的接口與外界交互,子類不應該隨便破壞它。
3、依賴倒置原則(Dependence Inversion Principle)
1、上層模塊不應該依賴底層模塊,它們都應該依賴於抽象。
2、抽象不應該依賴於細節,細節應該依賴於抽象。
面向接口編程,依賴於抽象而不依賴於具體。寫代碼時用到具體類時,不與具體類交互,而與具體類的上層接口交互。
4、接口隔離原則(Interface Segregation Principle)
1、客户端不應該依賴它不需要的接口。
2、類間的依賴關係應該建立在最小的接口上。
每個接口中不存在子類用不到卻必須實現的方法,如果不然,就要將接口拆分。使用多個隔離的接口,比使用單個接口(多個接口方法集合到一個的接口)要好。
5、迪米特法則(最少知道原則)(Law of Demeter)
只與你的直接朋友交談,不跟“陌生人”説話。
一個類對自己依賴的類知道的越少越好。無論被依賴的類多麼複雜,都應該將邏輯封裝在方法的內部,通過 public 方法提供給外部。這樣當被依賴的類變化時,才能最小的影響該類。
最少知道原則的另一個表達方式是:只與直接的朋友通信。類之間只要有耦合關係,就叫朋友關係。耦合分為依賴、關聯、聚合、組合等。我們稱出現為成員變量、方法參數、方法返回值中的類為直接朋友。局部變量、臨時變量則不是直接的朋友。我們要求陌生的類不要作為局部變量出現在類中。
6、合成複用原則(Composite Reuse Principle)
儘量使用對象組合/聚合,而不是繼承關係達到軟件複用的目的。
合成或聚合可以將已有對象納入到新對象中,使之成為新對象的一部分,因此新對象可以調用已有對象的功能。
| 原則 | 含義 |
|---|---|
| 單一職責原則(SRP) | 一個類只負責一項職責 |
| 開放封閉原則(OCP) | 對擴展開放,對修改關閉 |
| 里氏替換原則(LSP) | 子類能替換父類並正常工作 |
| 依賴倒置原則(DIP) | 面向接口編程,依賴抽象而非具體實現 |
| 接口隔離原則(ISP) | 接口應小而專,不要強迫實現不需要的方法 |
| 迪米特法則(LoD) | 最少知道原則,只與直接朋友通信 |
記憶口訣: 單開裏,依隔米(SRP、OCP、LSP、DIP、ISP、LoD)
三、設計模式的三大類
創建型模式(Creational Pattern):對類的實例化過程進行了抽象,能夠將軟件模塊中對象的創建和對象的使用分離。
(5 種)工廠模式、抽象工廠模式、單例模式、建造者模式、原型模式
結構型模式(Structural Pattern):關注於對象的組成以及對象之間的依賴關係,描述如何將類或者對象結合在一起形成更大的結構,就像搭積木,可以通過簡單積木的組合形成複雜的、功能更為強大的結構。
(7 種)適配器模式、裝飾者模式、代理模式、外觀模式、橋接模式、組合模式、享元模式
行為型模式(Behavioral Pattern):關注於對象的行為問題,是對在不同的對象之間劃分責任和算法的抽象化;不僅僅關注類和對象的結構,而且重點關注它們之間的相互作用。
(11 種)策略模式、模板方法模式、觀察者模式、迭代器模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式
| 類別 | 口訣 |
|---|---|
| 創建型 | 單工建原抽(單例、工廠、建造、原型、抽象工廠) |
| 結構型 | 適橋裝外享組代(適配器、橋接、裝飾、外觀、享元、組合、代理) |
| 行為型 | 策模觀責狀 命備媒解迭訪(策略、模板、觀察者、責任鏈、狀態、命令、備忘錄、中介者、解釋器、迭代器、訪問者) |
🎤 口訣舉例記憶法:
“單工建原抽 造對象,適橋裝外享組代 結構強,策模觀責狀 命備媒解迭訪 行為全。”
四、23 種設計模式
| 類別 | 模式 | 場景舉例(面試回答) | 一句話記憶 |
|---|---|---|---|
| 創建型 | 單例 | 日誌、配置、線程池 | 只此一個 |
| 工廠方法 | JDBC 獲取連接 | 接口造對象 | |
| 抽象工廠 | 跨平台 UI 組件 | 族類批發 | |
| 建造者 | 商品定製、表單填寫 | 分步構造 | |
| 原型 | Word 模板、克隆人 | 快速複製 | |
| 結構型 | 適配器 | 老接口兼容新標準 | 接口翻譯器 |
| 橋接 | 遙控器適配不同電視 | 解耦分層 | |
| 裝飾器 | IO 流、增強功能 | 動態增強 | |
| 外觀 | 封裝子系統 | 一鍵總控 | |
| 享元 | 樹、字母、棋子 | 內存共享 | |
| 組合 | 文件夾樹、菜單樹 | 樹形結構 | |
| 代理 | RPC 調用、權限 | 控制訪問 | |
| 行為型 | 策略 | 支付方式切換 | 多算法切換 |
| 模板方法 | JDBC Template | 父類定流程 | |
| 觀察者 | 事件通知、訂閲推送 | 發佈訂閲 | |
| 責任鏈 | 日誌、攔截器鏈 | 一問到底 | |
| 狀態 | 審批流程、售貨機 | 狀態切行為 | |
| 命令 | 撤銷、按鈕命令 | 請求打包 | |
| 備忘錄 | 編輯撤銷、快照 | 回檔備份 | |
| 中介者 | UI 控件協調、聊天室 | 調度中心 | |
| 解釋器 | 正則、SQL 語法分析 | 小語言 | |
| 迭代器 | Java 集合 | 順序訪問 | |
| 訪問者 | 編譯器遍歷結構 | 分離操作 |
創建型模式
| 模式 | 描述 | 應用實例 | 設計原則 |
|---|---|---|---|
| 單例模式 Singleton | 保證類只有一個實例,全局共享 | 日誌、數據庫連接池、配置中心 | SRP、DIP |
| 工廠方法 Factory Method | 定義創建對象的接口,由子類決定實例化哪個類 | JDBC 獲取連接,Spring Bean 創建 | OCP、DIP |
| 抽象工廠 Abstract Factory | 提供創建一系列相關或依賴對象的接口 | GUI 跨平台開發(Windows/Mac) | OCP、DIP |
| 建造者 Builder | 將對象構建過程與表示分離,逐步構建複雜對象 | 創建複雜表單、商品訂單生成 | SRP、OCP |
| 原型 Prototype | 通過複製已有實例來創建新對象 | 克隆 Excel 模板、遊戲角色複製 | SRP、OCP |
🔑 記憶口訣:“單工建原抽,共創對象路。
工廠模式
工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。
在工廠模式中,我們在創建對象時不會對客户端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。
定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。
應用實例:您需要一輛汽車,可以直接從工廠裏面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裏面的具體實現。而至於需要哪個牌子的汽車,就到哪個牌子的工廠。
抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創建其他工廠。該超級工廠又稱為其他工廠的工廠。
在抽象工廠模式中,接口是負責創建一個相關對象的工廠,不需要顯式指定它們的類。每個生成的工廠都能按照工廠模式提供對象。
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
應用實例:對於一個家庭來説,可能有商務女裝、商務男裝、時尚女裝、時尚男裝,都是成套的,即一系列具體產品。假設一種情況,在您的家中,某一個衣櫃(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產品),每次拿這種成套的衣服時也自然要從這個衣櫃中取出了。用 OO 的思想去理解,所有的衣櫃(具體工廠)都是衣櫃類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體產品),褲子(某一具體產品),這些具體的上衣其實也都是上衣(抽象產品),具體的褲子也都是褲子(另一個抽象產品)。
單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
應用實例:一個班級只能有一個班主任。
建造者模式(構建者模式)
建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個複雜的對象。
一個 Builder 類會一步一步構造最終的對象。該 Builder 類是獨立於其他對象的。
將一個複雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。
應用實例:
1、去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的“套餐”;
2、Java 中的 StringBuilder。
原型模式
原型模式(Prototype Pattern)是用於創建重複的對象,同時又能保證性能。
這種模式是實現了一個原型接口,該接口用於創建當前對象的克隆。當直接創建對象的代價比較大時,則採用這種模式。例如,一個對象需要在一個高代價的數據庫操作之後被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫調用。
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
應用實例:
1、細胞分裂;
2、Java 中的 Object clone() 方法。
結構型模式
| 模式 | 描述 | 應用實例 | 設計原則 |
|---|---|---|---|
| 適配器 Adapter | 轉換接口,使原接口兼容新接口 | 老版本電源適配器、接口兼容 | OCP、ISP |
| 橋接 Bridge | 分離抽象和實現,解耦兩個變化維度 | 不同品牌遙控器控制不同電視 | DIP、OCP |
| 裝飾器 Decorator | 動態擴展類的功能,保持原類結構不變 | Java IO 流,AOP | OCP、SRP |
| 外觀 Facade | 提供統一接口簡化子系統操作 | Spring 中的 Facade 層 | LoD |
| 享元 Flyweight | 共享細粒度對象,節省內存 | 遊戲中重複的敵人、圖標 | SRP、OCP |
| 組合 Composite | 將對象組合成樹狀結構,統一對待個體和整體 | 菜單欄、組織結構樹 | LSP、OCP |
| 代理 Proxy | 控制對對象的訪問,增加額外控制 | RPC、權限控制、延遲加載 | SRP、DIP |
🔑 記憶口訣:“適橋裝外享,組合代理不離場。”
適配器模式
適配器模式(Adapter Pattern)是作為兩個不兼容的接口之間的橋樑。
這種模式涉及到一個單一的類,該類負責加入獨立的或不兼容的接口功能。
將一個類的接口轉換成客户希望的另外一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
應用實例:
1、讀卡器是作為內存卡和筆記本之間的適配器。您將內存卡插入讀卡器,再將讀卡器插入筆記本,這樣就可以通過筆記本來讀取內存卡;
2、美國電器 110V,中國 220V,就要有一個變壓器將 110V 轉化為 220V。
裝飾器模式
裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。
這種模式創建了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
動態地給一個對象添加一些額外的職責。就增加功能來説,裝飾器模式相比生成子類更為靈活。
應用實例:
1、孫悟空有 72 變,當他變成"廟宇"後,他的根本還是一隻猴子,但是他又有了廟宇的功能;
2、將一個形狀裝飾上不同的顏色,同時又不改變形狀。
代理模式
在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。
在代理模式中,我們創建具有現有對象的對象,以便向外界提供功能接口。
為其他對象提供一種代理以控制對這個對象的訪問。
應用實例:
1、Windows 裏面的快捷方式;
2、買火車票不一定在火車站買,也可以去代售點;
3、一張支票或銀行存單是賬户中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人賬號上資金的控制;
4、Spring AOP。
注意事項:
1、和適配器模式的區別:適配器模式主要改變所考慮對象的接口,而代理模式不能改變所代理類的接口。
2、和裝飾器模式的區別:裝飾器模式為了增強功能,而代理模式是為了加以控制。
外觀模式
外觀模式(Facade Pattern)隱藏系統的複雜性,並向客户端提供了一個客户端可以訪問系統的接口。
這種模式涉及到一個單一的類,該類提供了客户端請求的簡化方法和對現有系統類方法的委託調用。
為子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
應用實例:
去醫院看病,可能要去掛號、門診、劃價、取藥,讓患者或患者家屬覺得很複雜,如果有提供接待人員,只讓接待人員來處理,就很方便。
橋接模式
橋接模式(Bridge Pattern)是用於把抽象化與實現化解耦,使得二者可以獨立變化。它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。
這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立於接口實現類。這兩種類型的類可被結構化改變而互不影響。
將抽象部分與實現部分分離,使它們都可以獨立的變化。
又稱為柄體(Handle and Body)模式或接口(Interface)模式。
應用實例:
1、豬八戒從天蓬元帥轉世投胎到豬,轉世投胎的機制將塵世劃分為兩個等級,即:靈魂和肉體,前者相當於抽象化,後者相當於實現化。生靈通過功能的委派,調用肉體對象的功能,使得生靈可以動態地選擇;
2、牆上的開關,可以看到的開關是抽象的,不用管裏面具體怎麼實現的;
3、如果要繪製不同的顏色,如紅色、綠色、藍色的矩形、圓形、橢圓、正方形,我們需要根據實際需要對形狀和顏色進行組合,那麼顏色、形狀就是抽象部分,組合後的就是實現部分。
注意事項:對於兩個獨立變化的維度,使用橋接模式再適合不過了。
組合模式
組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬於結構型模式,它創建了對象組的樹形結構。
這種模式創建了一個包含自己對象組的類。該類提供了修改相同對象組的方式。
將對象組合成樹形結構以表示"部分-整體"的層次結構。組合模式使得用户對單個對象和組合對象的使用具有一致性。
應用實例:
1、算術表達式包括操作數、操作符和另一個操作數,其中,另一個操作數也可以是操作數、操作符和另一個操作數。
2、在 JAVAAWT 和 SWING 中,對於 Button 和 Checkbox 是樹葉,Container 是樹枝。
享元模式
享元模式(Flyweight Pattern)主要用於減少創建對象的數量,以減少內存佔用和提高性能。這種類型的設計模式屬於結構型模式,它提供了減少對象數量從而改善應用所需的對象結構的方式。
享元模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。
運用共享技術有效地支持大量細粒度的對象。
應用實例:
1、Java 中的 String,如果有則返回,如果沒有則創建一個字符串保存在字符串緩存池裏面;
2、數據庫的數據池。
行為型模式
| 模式 | 描述 | 應用實例 | 設計原則 |
|---|---|---|---|
| 策略 Strategy | 定義一系列算法,互相替換 | 支付方式切換(支付寶、微信) | OCP、ISP |
| 模板方法 Template Method | 定義算法骨架,部分步驟由子類實現 | Spring 中的 JdbcTemplate | OCP |
| 觀察者 Observer | 發佈-訂閲模式,狀態變化通知觀察者 | 消息訂閲、事件監聽器 | DIP、LoD |
| 責任鏈 Chain of Responsibility | 請求沿鏈傳遞,直到有對象處理它 | Servlet 過濾器、日誌責任鏈 | SRP、LoD |
| 狀態 State | 改變對象狀態即改變其行為 | 審批流程、遊戲角色狀態 | SRP、OCP |
| 命令 Command | 將請求封裝成對象,支持撤銷/隊列 | 撤銷操作、按鈕點擊命令 | OCP、DIP |
| 備忘錄 Memento | 保存對象狀態,支持恢復 | 編輯器撤銷、遊戲存檔 | SRP |
| 中介者 Mediator | 用中介統一管理對象間通信 | UI 組件協調、聊天室中轉 | LoD、SRP |
| 解釋器 Interpreter | 自定義語言解釋器 | 正則表達式解析器、SQL 語法樹 | OCP |
| 迭代器 Iterator | 提供訪問集合內部元素的方法 | Java 集合迭代器 | ISP、SRP |
| 訪問者 Visitor | 將操作從數據結構中分離 | 複雜結構遍歷(如 AST) | OCP、LSP |
🔑 記憶口訣:“策模觀責狀,命備媒解迭訪,行為全靠它。”
策略模式
在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。
在策略模式中,我們創建表示各種策略的對象和一個行為隨着策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。
定義一系列的算法,把它們一個個封裝起來,並且使它們可相互替換。
應用實例:
1、諸葛亮的錦囊妙計,每一個錦囊就是一個策略;
2、旅行的出遊方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略。
模板模式
在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
應用實例:
1、在造房子的時候,地基、走線、水管都一樣,只有在建築的後期才有加壁櫥加柵欄等差異;
2、西遊記裏面菩薩定好的 81 難,這就是一個頂層的邏輯骨架;
3、spring 中對 Hibernate 的支持,將一些已經定好的方法封裝起來,比如開啓事務、獲取 Session、關閉 Session 等,程序員不重複寫那些已經規範好的代碼,直接丟一個實體就可以保存。
觀察者模式
當對象間存在一對多關係時,則使用觀察者模式(ObserverPattern)。比如,當一個對象被修改時,則會自動通知它的依賴對象。
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
應用實例:
1、拍賣的時候,拍賣師觀察最高標價,然後通知給其他競價者競價;
2、西遊記裏面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜,這個烏龜就是觀察者,他觀察菩薩灑水這個動作。
迭代器模式
迭代器模式(Iterator Pattern)是 Java 和.Net 編程環境中非常常用的設計模式。這種模式用於順序訪問集合對象的元素,不需要知道集合對象的底層表示。
迭代器模式屬於行為型模式。
提供一種方法順序訪問一個聚合對象中各個元素,而又無須暴露該對象的內部表示。
應用實例:JAVA 中的 iterator。
責任鏈模式
顧名思義,責任鏈模式(Chain of Responsibility Pattern)為請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。
在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。
避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,並且沿着這條鏈傳遞請求,直到有對象處理它為止。
應用實例:紅樓夢中的"擊鼓傳花"。
命令模式
命令模式(Command Pattern)是一種數據驅動的設計模式。請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。
將一個請求封裝成一個對象,從而使您可以用不同的請求對客户進行參數化。
應用實例:電視機是請求的接收者,遙控器是請求的發送者,遙控器上有一些按鈕,不同的按鈕對應電視機的不同操作。抽象命令角色由一個命令接口來扮演,有三個具體的命令類實現了抽象命令接口,這三個具體命令類分別代表三種操作:打開電視機、關閉電視機和切換頻道。
備忘錄模式
備忘錄模式(Memento Pattern)保存一個對象的某個狀態,以便在適當的時候恢復對象。
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。
應用實例:
1、後悔藥;
2、打遊戲時的存檔;
3、Windows 裏的 ctri+z;
4、IE 中的後退;
5、數據庫的事務管理。
狀態模式
在狀態模式(State Pattern)中,類的行為是基於它的狀態改變的。
在狀態模式中,我們創建表示各種狀態的對象和一個行為隨着狀態對象改變而改變的 context 對象。
允許對象在內部狀態發生改變時改變它的行為,對象看起來好像修改了它的類。
應用實例:
1、打籃球的時候運動員可以有正常狀態、不正常狀態和超常狀態;
2、曾侯乙編鐘中,'鍾是抽象接口','鍾 A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。
訪問者模式
在訪問者模式(Visitor Pattern)中,我們使用了一個訪問者類,它改變了元素類的執行算法。通過這種方式,元素的執行算法可以隨着訪問者改變而改變。根據模式,元素對象已接受訪問者對象,這樣訪問者對象就可以處理元素對象上的操作。
主要將數據結構與數據操作分離。
主要解決:穩定的數據結構和易變的操作耦合問題。
應用實例:您在朋友家做客,您是訪問者,朋友接受您的訪問,您通過朋友的描述,然後對朋友的描述做出一個判斷,這就是訪問者模式。
中介者模式
中介者模式(Mediator Pattern)是用來降低多個對象和類之間的通信複雜性。這種模式提供了一箇中介類,該類通常處理不同類之間的通信,並支持鬆耦合,使代碼易於維護。
用一箇中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。
應用實例:
1、中國加入 WTO 之前是各個國家相互貿易,結構複雜,現在是各個國家通過 WTO 來互相貿易;
2、機場調度系統;
3、MVC 框架,其中 C(控制器)就是 M(模型)和 V(視圖)的中介者。
解釋器模式
解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。
給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。
應用實例:編譯器、運算表達式計算。
面試高頻問答
Q:你瞭解哪些設計模式?
A:我熟悉創建型、結構型、行為型三類設計模式,常用的有:
- 單例模式 用於全局唯一對象(如配置、日誌);
- 工廠模式 用於對象的解耦創建(如數據庫連接);
- 策略模式 可實現算法或行為的靈活替換(如支付方式);
- 觀察者模式 常用於事件通知系統(如監聽器);
- 裝飾器模式 可動態增強功能(如過濾器鏈);
- 模板方法 用於定義算法骨架(如 JDBC Template)。
Q:如何理解開放封閉原則?你項目裏是如何實踐的?
A:開放封閉原則要求系統對擴展開放,對修改關閉。設計模式中策略模式、裝飾器模式、工廠模式都很好地體現了這一點。比如策略模式通過引入接口,可以添加新的策略類而不修改原有邏輯,方便擴展。
我在實踐中採用如下方式:
- 使用接口 + 策略模式,對不同規則進行封裝
- 利用裝飾器模式增強功能時,不修改原代碼
- 利用責任鏈動態組合處理器,不改主流程
Q:什麼是單例?怎麼實現線程安全的單例?
A:單例模式確保一個類只有一個實例並提供全局訪問點。在 Java 中常用如下線程安全方式:
- 餓漢式(類加載時初始化,線程安全)
- 雙重檢查鎖懶漢式(結合 volatile + synchronized)
- 靜態內部類方式(推薦,延遲加載 + 線程安全)
應用場景: 日誌記錄器、配置中心、線程池
Q:怎麼選擇使用哪種設計模式?你怎麼理解“不是為了模式而模式”?
A:
設計模式是經驗總結,而不是強制規範。選擇依據:
- 是否可複用:代碼是否會被多處使用?
- 是否可擴展:未來是否頻繁變化?
- 是否符合 OOP 原則:職責單一、解耦?
不建議為了套用模式而增加結構複雜度。應當基於具體業務和演進需求靈活運用。
Q: 什麼是高內聚、低耦合?設計模式如何體現?
A:
高內聚:類的職責集中,專注處理自己的事務。
低耦合:類與類之間依賴少、聯繫弱。
設計模式(如策略、工廠、觀察者)通過接口隔離、組合優於繼承等方式實現了高內聚低耦合。
Q:Spring 框架用了哪些設計模式?
| 設計模式 | Spring 中的應用示例 | 類型 |
|---|---|---|
| 工廠模式 | BeanFactory、ApplicationContext 創建對象 |
創建型 |
| 單例模式 | 默認 Spring Bean 是單例的 | 創建型 |
| 模板方法模式 | JdbcTemplate、RestTemplate 預定義流程 + 鈎子方法 |
行為型 |
| 代理模式 | AOP 實現(JDK 動態代理 / CGLIB) | 結構型 |
| 策略模式 | Bean 的選擇策略、ResourceLoader、TransactionManager | 行為型 |
| 觀察者模式 | ApplicationEvent + ApplicationListener 事件機制 |
行為型 |
| 責任鏈模式 | Spring Security Filter、SpringMVC HandlerInterceptor | 行為型 |
| 裝飾器模式 | BeanWrapper、增強類鏈式處理(如緩存) | 結構型 |
| 適配器模式 | HandlerAdapter 適配不同 Controller |
結構型 |
| 橋接模式 | Jdbc 抽象和具體實現之間的橋接(驅動與邏輯分離) | 結構型 |
| 建造者模式 | BeanDefinitionBuilder、MockMvcBuilders 等 |
創建型 |
| 享元模式 | IOC 容器中 Bean 的共享(默認單例) | 結構型 |
Q: JDK 中都用了哪些設計模式?
| 設計模式 | Java 應用場景 | 類型 |
|---|---|---|
| 單例模式 | Runtime.getRuntime()、Desktop.getDesktop() |
創建型 |
| 工廠模式 | Calendar.getInstance()、NumberFormat.getInstance() |
創建型 |
| 抽象工廠模式 | DocumentBuilderFactory、SQL ConnectionFactory |
創建型 |
| 建造者模式 | StringBuilder、Stream.Builder |
創建型 |
| 原型模式 | Object.clone()、Cloneable 接口 |
創建型 |
| 模板方法模式 | AbstractList, InputStream.read() 中的模板方法 |
行為型 |
| 迭代器模式 | Iterator, Enumeration 接口 |
行為型 |
| 觀察者模式 | Observable + Observer(已過時) |
行為型 |
| 策略模式 | Comparator 接口、Collections.sort() |
行為型 |
| 適配器模式 | InputStreamReader(將字節流轉為字符流) |
結構型 |
| 裝飾器模式 | BufferedReader, BufferedInputStream |
結構型 |
| 代理模式 | Proxy.newProxyInstance()、動態代理 API |
結構型 |
| 組合模式 | AWT/Swing 中的 Component 組合 UI 元素 |
結構型 |
| 責任鏈模式 | Servlet Filter 鏈、Logging API |
行為型 |