設計模式速記-設計模式概述
一、概述
1. 什麼是設計模式
設計模式是一套被反複使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。它描述了在軟體設計過程中的一些不斷重複發生的問題,以及該問題的解決方案。也就是說,它是解決特定問題的一系列套路,是前輩們的代碼設計經驗的總結,具有一定的普遍性,可以反複使用。其目的是為了提高代碼的可重用性、代碼的可讀性和代碼的可靠性。
2. 學習設計模式的意義
設計模式的本質是面向對象設計原則的實際運用,是對類的封裝性、繼承性和多态性以及類的關聯關系群組合關系的充分了解。正确使用設計模式具有以下優點。
- 可以提高程式員的思維能力、程式設計能力和設計能力。
- 使程式設計更加标準化、代碼編制更加工程化,使軟體開發效率大大提高,進而縮短軟體的開發周期。
- 使設計的代碼可重用性高、可讀性強、可靠性高、靈活性好、可維護性強。
當然,軟體設計模式隻是一個引導。在具體的軟體幵發中,必須根據設計的應用系統的特點和要求來恰當選擇。對于簡單的程式開發,苛能寫一個簡單的算法要比引入某種設計模式更加容易。但對大項目的開發或者架構設計,用設計模式來組織代碼顯然更好。
二、設計模式分類
1. 按照目的劃分
使用設計模式時根據使用該模式的目的劃分,可以将設計模式劃分為三種類型,分别是建立型模式、結構型模式 和行為型模式。
- 建立型模式:
建立型模式用于描述**“怎樣建立對象”,它的特點是"将對象的使用和建立分離"**,GoF共提供了5種建立型模式:
- 單例模式
- 原型模式
- 工廠模式
- 抽象工廠模式
- 建造者模式
- 結構型模式:
結構型模式用于将類或對象按照某種布局組合成更大的結構,GoF種共提供了7種結構型模式:
- 代理模式
- 擴充卡模式
- 橋接模式
- 裝飾器模式
- 外觀模式
- 享元模式
- 組合模式
- 行為型模式:
行為型模式用于描述類或對象之間怎樣互相協作共同完成單個對象都無法單獨完成的任務,以及怎樣配置設定職責 ,GoF種共提供了11種行為型模式:
- 模闆方法模式
- 政策模式
- 指令模式
- 職責鍊模式
- 狀态模式
- 觀察者模式
- 中介者模式
- 疊代器模式
- 通路者模式
- 備忘錄模式
- 解釋器模式
2. 按照作用範圍劃分
根據模式是作用在類上還是對象上來劃分,将設計模式分為類模式和對象模式兩種。
- 類模式
用于處理類和子類之間的關系,通過繼承來建立,并且是靜态的,在編譯時便确定下來。共有4種設計模式屬于類模式:工廠模式、擴充卡模式、模闆方法模式、解釋器模式。
- 對象模式
用于處理對象之間的關系,這些關系可以通過聚合或組合來實作,運作時是可變化的,具有動态性。GoF種除了上述4種類模式之外全部屬于對象模式。
三、23種設計模式簡述
模式名 | 英文 | 簡述 |
---|---|---|
單例模式 | Singleton | 某個類隻能生成一個執行個體,該類提供了一個全局通路點供外部擷取該執行個體,其拓展是有限多例模式。 |
原型模式 | Prototype | 将一個對象作為原型,通過對其進行複制而克隆出多個和原型類似的新執行個體。 |
工廠方法模式 | Factory Method | 定義一個用于建立産品的接口,由子類決定生産什麼産品。 |
抽象工廠模式 | Abstract Factory | 提供一個建立産品族的接口,其每個子類可以生産一系列相關的産品。 |
建造者模式 | Builder | 将一個複雜對象分解成多個相對簡單的部分,然後根據不同需要分别建立它們,最後建構成該複雜對象。 |
代理模式 | Proxy | 為某對象提供一種代理以控制對該對象的通路。即用戶端通過代理間接地通路該對象,進而限制、增強或修改該對象的一些特性。 |
擴充卡模式 | Adapter | 将一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不相容而不能一起工作的那些類能一起工作。 |
橋接模式 | Bridge | 将抽象與實作分離,使它們可以獨立變化。它是用組合關系代替繼承關系來實作,進而降低了抽象和實作這兩個可變次元的耦合度。 |
裝飾器模式 | Decorator | 動态的給對象增加一些職責,即增加其額外的功能。 |
外觀模式 | Facade | 為多個複雜的子系統提供一個一緻的接口,使這些子系統更加容易被通路。 |
享元模式 | Flyweight | 運用共享技術來有效地支援大量細粒度對象的複用。 |
組合模式 | Composite | 将對象組合成樹狀層次結構,使使用者對單個對象群組合對象具有一緻的通路性。 |
模闆方法模式 | Template Method | 定義一個操作中的算法骨架,而将算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。 |
政策模式 | Strategy | 定義了一系列算法,并将每個算法封裝起來,使它們可以互相替換,且算法的改變不會影響使用算法的客戶。 |
指令模式 | Command | 将一個請求封裝為一個對象,使送出請求的責任和執行請求的責任分割開。 |
職責鍊模式 | Chain of Responsibility | 把請求從鍊中的一個對象傳到下一個對象,直到請求被響應為止。通過這種方式去除對象之間的耦合。 |
狀态模式 | State | 允許一個對象在其内部狀态發生改變時改變其行為能力。 |
觀察者模式 | Observer | 多個對象間存在一對多關系,當一個對象發生改變時,把這種改變通知給其他多個對象,進而影響其他對象的行為。 |
中介者模式 | Mediator | 定義一個中介對象來簡化原有對象之間的互動關系,降低系統中對象間的耦合度,使原有對象之間不必互相了解。 |
疊代器模式 | Iterator | 提供一種方法來順序通路聚合對象中的一系列資料,而不暴露聚合對象的内部表示。 |
通路者模式 | Visitor | 在不改變集合元素的前提下,為一個集合中的每個元素提供多種通路方式,即每個元素有多個通路者對象通路。 |
備忘錄模式 | Memento | 在不破壞封裝性的前提下,擷取并儲存一個對象的内部狀态,以便以後恢複它。 |
解釋器模式 | Interpreter | 提供如何定義語言的文法,以及對語言句子的解釋方法,即解釋器。 |
四、設計模式原則
1. 開閉原則
概念與意義
開閉原則即: 軟體實體應當對擴充開放,對修改關閉 .換句話說,開閉原則即是軟體的應用需求改變時,在不修改軟體源代碼的情況下,通過擴充子產品的功能,使其滿足新需求的功能.
作用
開閉原則使軟體實體擁有一定的适應性、靈活性、穩定性和延續性,具體表現的作用如下:
-
友善代碼測試
軟體遵守開閉原則後,在測試時隻需要對新擴充代碼進行測試,而不必重新測試已有代碼。
-
提高代碼複用性
代碼粒度越小,則代碼的可複用性越好。
-
提高軟體可維護性
遵守開閉原則,軟體的穩定性和靈活性提高,進而易于維護和擴充。
實作方法
通過***抽象限制、封裝變化***的方法來實作開閉原則。
-
抽象限制
通過接口或抽象類為軟體實體定義一個相對穩定的抽象層,用來限制子類。
-
封裝變化
将功能方法封裝在***抽象限制***的接口/抽象類的子類中,這樣接口或抽象類的方法可以保持不變,而每增加一個新需求可以通過建立一個新的實作該接口/抽象類的子類來滿足需求。
2. 裡氏替換原則
概念與意義
裡氏替換原則即: 繼承必須確定超類所擁有的性質在子類中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。
作用
- 裡氏替換原則是實作開閉原則的重要方式之一。
- 它克服了繼承中重寫父類造成的可複用性變差的缺點。
- 它是動作正确性的保證。即類的擴充不會給已有的系統引入新的錯誤,降低了代碼出錯的可能性。
實作方法
裡氏替換原則的具體實作方法在實踐中即是:子類可以擴充父類的功能,但是不能改變父類的功能 ,也就是***繼承不重寫***
3. 依賴倒置原則
概念與意義
依賴倒置原則即:
高層子產品不應該依賴低層子產品,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)。其核心思想是:要面向接口程式設計,不要面向實作程式設計。
作用
- 依賴倒置原則可以降低類間的耦合性。
- 依賴倒置原則可以提高系統的穩定性。
- 依賴倒置原則可以減少并行開發引起的風險。
- 依賴倒置原則可以提高代碼的可讀性和可維護性。
實作方法
依賴倒置原則可以通過面向接口程式設計來降低類之間的耦合性,實作方法如下:
- 每個類盡量提供接口或抽象類,或者兩者都具備。
- 變量的聲明類型盡量是接口或者是抽象類。
- 任何類都不應該從具體類派生。
- 使用繼承時盡量遵循裡氏替換原則。
4. 單一職責原則
概念
單一職責原則規定一個類應該有且僅有一個引起它變化的原因,否則類應該被拆分(There should never be more than one reason for a class to change)
作用
- 降低類的複雜度。一個類隻負責一項職責,其邏輯肯定要比負責多項職責簡單得多。
- 提高類的可讀性。複雜性降低,自然其可讀性會提高。
- 提高系統的可維護性。可讀性提高,那自然更容易維護了。
- 變更引起的風險降低。變更是必然的,如果單一職責原則遵守得好,當修改一個功能時,可以顯著降低對其他功能的影響。
實作方法
單一職責原則的核心就是控制類的粒度大小、将對象解耦、提高其内聚性。在實踐過程中應該把握類的功能,将類的不同職責分離後,再封裝到其他的類中。即類隻具有符合其自身職責的功能,不屬于其自身職責的功能應拆分到其他類中實作。
5. 接口隔離原則
概念
接口隔離原則要求開發者盡可能将功能龐大的接口拆分成更小、更具體的接口。其接口中隻包含讓使用該接口的用戶端感興趣的方法。即:
一個類對另一個類的依賴應該建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)
作用
- 将臃腫龐大的接口分解為多個粒度小的接口,可以預防外來變更的擴散,提高系統的靈活性和可維護性。
- 接口隔離提高了系統的内聚性,減少了對外互動,降低了系統的耦合性。
- 如果接口的粒度大小定義合理,能夠保證系統的穩定性;但是,如果定義過小,則會造成接口數量過多,使設計複雜化;如果定義太大,靈活性降低,無法提供定制服務,給整體項目帶來無法預料的風險。
- 使用多個專門的接口還能夠展現對象的層次,因為可以通過接口的繼承,實作對總接口的定義。
- 能減少項目工程中的代碼備援。過大的大接口裡面通常放置許多不用的方法,當實作這個接口的時候,被迫設計備援的代碼。
實作方法
在具體應用接口隔離原則時,應該根據以下幾個規則來衡量。
- 接口盡量小,但是要有限度。一個接口隻服務于一個子子產品或業務邏輯。
- 為依賴接口的類定制服務。隻提供調用者需要的方法,屏蔽不需要的方法。
- 了解環境,拒絕盲從。每個項目或産品都有標明的環境因素,環境不同,接口拆分的标準就不同深入了解業務邏輯。
- 提高内聚,減少對外互動。使接口用最少的方法去完成最多的事情。
6. 迪米特法則
概念
如果兩個軟體實體無須直接通信,那麼就不應當發生直接的互相調用,可以通過第三方轉發該調用。其目的是降低類之間的耦合度,提高子產品的相對獨立性。
作用
- 降低了類之間的耦合度,提高了子產品的相對獨立性。
- 由于親合度降低,進而提高了類的可複用率和系統的擴充性。
實作方法
從迪米特法則的定義和特點可知,它強調以下兩點:
- 從依賴者的角度來說,隻依賴應該依賴的對象。
- 從被依賴者的角度說,隻暴露應該暴露的方法。
是以,在運用迪米特法則時要注意以下 6 點。
- 在類的劃分上,應該建立弱耦合的類。類與類之間的耦合越弱,就越有利于實作可複用的目标。
- 在類的結構設計上,盡量降低類成員的通路權限。
- 在類的設計上,優先考慮将一個類設定成不變類。
- 在對其他類的引用上,将引用其他對象的次數降到最低。
- 不暴露類的屬性成員,而應該提供相應的通路器(set 和 get 方法)。
- 謹慎使用序列化(Serializable)功能。
7. 合成複用原則
概念
合成複用原則:軟體複用時,要盡量先使用組合或者聚合等關聯關系來實作,其次才考慮使用繼承關系來實作。
作用
- 它維持了類的封裝性。因為成分對象的内部細節是新對象看不見的,是以這種複用又稱為“黑箱”複用。
- 新舊類之間的耦合度低。這種複用所需的依賴較少,新對象存取成分對象的唯一方法是通過成分對象的接口。
- 複用的靈活性高。這種複用可以在運作時動态進行,新對象可以動态地引用與成分對象類型相同的對象。
實作方法
通過将已有的對象納入新對象中,作為新對象的成員對象來實作的,新對象可以調用已有對象的功能,進而達到複用。
五、參考資料
- 菜鳥教程
- C語言中文網