一句話介紹面向對象設計原則與23種設計模式

設計模式對於軟件的設計和架構是非常重要的一部分,這篇文章的目的在於幫助初學者更好的理解每一個設計模式的基本思想,並不能從根本上替代這門課程的學習。在學習設計模式的時候,每種設計模式的特點、優點、使用場景、UML圖、代碼實現都是比較重要的知識點。

設計模式是由面向對象的設計原則引出的,因此我們從面向對象設計原則開始,依次介紹其基本思想。

一、面向對象設計原則

面向對象設計原則告訴我們在面向對象編程中,什麼樣的設計纔是好的設計。在當今互聯網的應用中,需求是會持續更改的,或者我們的產品是從原型依次將功能進行擴展的,因此我們的產品在設計的時候必須對擴展是友好的。這也是這一部分的核心思想:讓我們在擴展功能時儘可能地方便。

  1. 單一職責原則 (Single Responsibility Principle): 一個類應該只有一個變化的原因,在設計的時候要將不同的職責封裝到不同的類中。(這條原則保證了面向對象設計的高內聚低耦合,讓無關的功能放在不同的類中,達到解耦和增強內聚性的目標)
  2. 開放-封閉原則 (Open-Closed Principle): 類、模塊、函數應該是可以擴展的,但是是不可修改的,主要依賴接口、繼承和多態來實現
  3. 里氏替換原則 (Liskov Substitution Principle): 任何基類出現的地方,子類一定可以出現,當子類可以完全替代基類的時候,使用繼承纔是合理的
  4. 接口分離原則 (Interface Segreagation Principle): 接口應該儘可能是原子的、內聚的,它提供的是獨立的服務;實現接口的時候不應該要強迫實現以後不會用的抽象方法
  5. 依賴倒置原則 (Dependence Inversion Principle): 高層模塊不應該依賴於底層模塊,兩者應該依賴於共同的接口
  6. 組合/聚合複用原則 (Composite/Aggregate Reuse Principle): 多用組合和聚合,少用繼承
  7. 最小知識原則 (Principle of Least Knowledge): 每個類應該對其他類保留有限的知識,也就是不要讓太多類耦合在一起

將上面的思想進行總結,可以歸納出一下幾點:

  • 多使用聚合和組合,儘量避免使用繼承,除非子類可以完全替代基類
  • 在設計類或接口的時候,要將相關的功能放在一起,並避免將不同的功能放在一起;對於其他類的方法的調用,要儘可能地少,以此來實現高內聚低耦合的設計
  • 高層模塊不應該依賴底層模塊,兩者應該實現共同的接口

然而,這樣的原則在設計上對我們的幫助還是微乎其微,因此誕生了設計模式,來幫助我們進行類的設計。

二、設計模式

設計模式一共分爲三種,行爲型設計模式(Behavioral Pattern)、構造型設計模式(Creational Pattern)、結構型設計模式(Structural Pattern)。

行爲型設計模式:

  1. 觀察者模式 (Observer Pattern):當一個類頻繁的發生變化,可以設計一個實體類接口(Subject)和一個觀察者接口(Observer),並對其進行實現,來進行解耦;Subject接口及其子類管理觀察者,Observer接口在觀察到發生變化後對Subject接口的子類進行更新
  2. 策略模式 (Strategy Pattern):通過讓一個類與一種或多種算法的接口進行聚合,通過多態來實現算法的更換 (例如打遊戲的難度選項,通過實現一個接口,來實現不同難度下敵人的分佈和等級情況)
  3. 命令模式 (Command Pattern):將一個請求封裝到一個對象中,並實現參數化、請求排隊、請求日誌、請求撤銷與恢復的功能 (例如MySQL的事務)
  4. 模板方法 (Template Method):準備一個抽象類,搭建起算法的框架,將一部分共有的功能實現,並將其他部分定義爲不同的抽象方法,來逼迫子類實現不同的抽象方法,以此來實現不同的邏輯 (我要製作一杯飲料,過程都是燒熱水,加一種東西[抽象方法],然後倒入熱水。在實現抽象方法的時候我可以實現加茶葉包或者速溶咖啡粉)
  5. 迭代器模式 (Iterator Method):提供一種不暴露底層實現的,可以以此訪問聚合對象中元素的方法 (例如Java裏ArrayList的iterator或Python的迭代器)
  6. 狀態模式 (State Pattern):一個對象在內部狀態改變的時候會改變其行爲,就像改變了這個類一樣。我們定義狀態的接口,並通過實現類來實現每個狀態下對象具體的行爲。不同的狀態可以用享元模式來減少內存消耗
  7. 責任鏈模式 (Chain of Responsibility):避免請求發送者和接收者之間的耦合,讓請求在一條責任鏈上傳遞,直到傳遞到一個可以處理開請求的類 (例如辦某項手續,卻被告知要找另一個部門的人,最終找到正確的部門才能辦理手續)
  8. 中介者模式 (Mediator Pattern):封裝了兩個對象之間相互交互的方法,減少兩個對象的耦合,並讓其中一個方法發生變化時,不會立即影響到其他對象
  9. 備忘錄模式 (Memento Pattern):將一個對象的狀態保存起來,在需要的時候再次還原到原來的狀態並使用 (例如訓練深度學習模型的時候,我們可以將訓練好的模型保存起來,在使用的時候直接加載,無須再次訓練)
  10. 訪問者模式 (Visitor Pattern):當一個對象內部有許多其他的對象聚合,我們對其訪問時不希望污染這些類,可以實現一個訪問者類,來訪問需要的參數 (例如公司年底對員工進行評價,對程序員的評價是代碼量,對產品經理的評價是KPI,我們可以設計一個員工接口,並分別實現程序員類Engineer和產品經理類Manager;設計一個訪問者類Visitor,來分別實現對Engineer類和Manager類的visit方法,來完成對不同標準的評價)
  11. 解釋器模式 (Interpreter Pattern):給定一個語言之後,解釋器模式定義一種可以解釋該語言的文法,並提供一個解釋器,客戶可以用這個解釋器來解釋該語言的句子(例如編程語言的編譯器)

構造型設計模式:

  1. 工廠方法 (Factory Method):定義一個接口來實現某個對象的實例化過程,但讓接口的子類來定義如何實例化 (例如一個披薩,每個披薩製作過程相似,但上面放的料不同,因此我們可以定義一個製作披薩的接口,並根據不同的披薩種類來實現該接口)
  2. 抽象工廠方法 (Abstract Factory Method):爲一組聚合或組合的對象創建一個接口,而不需要這些對象提供具體實現類 (工廠需要造A、B兩種轎車,需要輪胎、車門、引擎、車輛框架等零件,我們實現A、B轎車兩種工廠,並在底層根據輛車的不同來對這些類進行實例化)
  3. 單例模式 (Singleton Pattern):一個類只能實例化一個對象,我們只能獲取一個實例化後的對象的引用 (例如線程池,我們只需要一個對象)
  4. 建造者模式 (Builder Pattern):將產品的結構和產品的零件建造過程對客戶端隱藏起來,把對建造過程進行指揮的責任和具體建造者零件的責任分割開來,達到責任劃分和封裝的目的 (例如RTF文件可以轉換爲word文件、pdf文件或Markdown文件,在未來可能需要添加其他轉換的方式。我們實現每一種轉換方式的方法類,並在一個類中進行聚合)
  5. 原型模式 (Prototype Pattern):創建一個原型接口,實例化的時候重寫其clone方法;實例化該對象的時候,直接獲取該原型對象的複製

結構性設計模式:

  1. 裝飾器模式 (Decorator Pattern):通過繼承一個實現類的接口,來在該實現類的基礎上添加職責/屬性/功能
  2. 適配器模式 (Adapter Pattern):將一個接口與另一個不兼容的接口之間添加一個類,使得兩者變得兼容 (通常是方法的函數變得兼容)
  3. 門面模式 (Facade Pattern):一個系統內與用戶有關的接口可能過多,將這些接口聚合,爲用戶提供一個統一的使用接口
  4. 組合模式 (Composite Pattern):將一個有層次結構的對象用樹結構來表示,通常使用二叉樹 (使用二叉樹也可以表示多叉樹) (例如餐廳的菜單,首先分爲肉菜、素材、湯、主食、甜點,每一項下面可以有其他具體的菜餚,可以用樹結構來實現)
  5. 代理模式 (Proxy Pattern):爲某一個對象創建一個代理對象,由代理對象來控制原對象的引用 (例如我們有一臺打印機可以進行打印操作,可是我們想通過日誌文件記錄打印機每次操作是否正常,因此我建立一個打印機的代理對象,把打印機作爲一個屬性,並實現上傳日誌的操作)
  6. 享元模式 (Flyweight Pattern):一些相同且需要大量重複的對象,會佔據大量的內存空間。我們將其存儲在哈希表中,每次輸入對應的鍵,來獲取一個對象的拷貝 (例如遊戲中刷經驗的小怪,實際上每次殺死的都是一個小怪的複製)
  7. 橋接模式 (Bridge Pattern):將類的抽象部分與現實部分分離開,使其可以獨立的變化;或將功能層次結構與實現層次結構分離 (例如我要設計的類是帶顏色[黑、白、灰]的一種形狀[圓形、三角形、正方形],於其我將每一種圖形的每一種顏色都實現[需要實現9個類],我可以新建一個對象,把圖形和顏色當作兩個屬性,並進行任意組合[需要實現1個類])

注:有一些設計模式使用的比較少,工廠方法、單例模式、代理模式、裝飾器模式、適配器模式、模板方法、策略模式、觀察者模式是比較重要的設計模式,需要重點掌握

三、幾個易混淆的設計模式

  1. 裝飾器模式和代理模式的區別
    代理模式偏向於將一個類本身沒有能力做的事,需要其他類來干涉才能做成的事,偏向於對對象的控制;裝飾器模式偏向於功能的擴展,一個類的裝飾器仍然屬於該類範疇本身。例如一個嬰兒剛生下來不會吃飯,可以設計一個父母的類作爲代理;當到達一定年齡後,可以自己吃飯了,這時候不僅要自己吃飯,還要自己收拾盤子,可以設計一個裝飾器進行功能的擴展
  2. 工廠方法和抽象工廠方法的區別
    工廠方法只能有一種抽象產品,一個具體工廠類只能實例化一種具體產品類;抽象工廠方法可以有多種抽象產品,一個具體工廠類可以實例化多種具體產品類
  3. 狀態模式和策略模式的區別
    狀態模式是多個狀態對應多種不同的行爲,策略模式是一種狀態對應不同的行爲
  4. 享元模式和原型模式的區別
    兩種模式都是爲了減少內存消耗,對某些反覆使用的類進行拷貝;享元模式可以通過哈希表訪問多種不相關的類的對象,原型模式定義了一個圓形接口,可以實現該接口來獲得不同子類,並獲取其複製

參考資料

[1] 學校設計模式課程課件
[2] 單一職責原則
[3] 橋接模式
[4] 訪問者模式一篇就夠了
[5] 代理模式和裝飾器模式的區別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章