本教學將透過 Java 實例,一步一步向您講解設計模式的概念。
目的:降低程式碼複雜度、系統解耦合、提高可讀性
#意義:對於一個類別,只有一個引起該類別變化的原因;該類別的職責是唯一的,而這個職責是唯一引起其他類別變化的原因。
解決:將不同的職責封裝到不同的類別或模組中。當有新的需求將現有的職責分為顆粒度較小的職責的時候,應該及時對現有程式碼進行重構。當系統邏輯夠簡單,方法夠少,子類別夠少或後續關聯夠少時,也可以不必嚴格遵循你SRP原則,避免過度設計、顆粒化過於嚴重。
實例:電線類Wire為居民供電,電壓為220v;但是新的需求增加,電線也輸送高壓電,電壓為200kv,原有電線類可以增加方法實現擴充,這就違背了單一職責原則。可以提供基類,創建兩個衍生類,居民供電線、高壓輸電線。
目的:避免系統繼承體係被破壞
##意義:所有引用基底類別的地方必須能透明地使用其子類別的物件。
解決:子類別可以實作父類別的抽象方法,但是不能覆寫父類別的非抽象方法;子類別中可以增加自己特有的方法;當子類別覆寫或實現父類別的方法時,方法的前置條件(即方法的形參)要比父類別方法的輸入參數更寬鬆;當子類別的方法實作父類別的抽象方法時,方法的後置條件(即方法的回傳值)要比父類別更嚴格。如果子類別不能完整地實作父類別的方法,或者父類別的一些方法在子類別中已經發生畸變,則建議斷開繼承關係,採用依賴,聚合,組合等關係代替繼承。
實例:已經定義鳥類具有兩個翅膀飛的方法;新加入鴕鳥,不會飛,如果覆蓋父類的方法,在兩個翅膀飛的方法中什麼也不做,就違反里氏替換原則,導致所有鳥都不會飛。應該創建並列的兩種鳥基類,會飛與不會飛的。前置條件更寬鬆、後置條件更嚴格,例如父類回傳Map,子類回傳HashMap;父類接受HashMap形參,子類接受Map。
3.依賴倒轉原則(Dependence Inversion Principle)目的:避免需求變化導致過多的維護工作
意思: 高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽像不應該依賴細節;細節應該依賴抽象。
解決:面向介面編程,使用介面或抽象類別制定好規範和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實作類別去完成。
實例:母親類別Mother有說故事方法TellStory,依賴一個Book類別輸入,並使用了Book類別的getContent方法以便講故事。那麼下次需要母親講報紙上的故事、手機上的故事時,原有接口無能為力。這時,抽象化一個包含getContent方法的IReader基類,Book、Newspaper、Cellphone各自實作。母親的TellStory方法接受一個IReader實例,並且呼叫getContent方法即可。
4.介面隔離原則(Interface Segregation Principle)# 目的:避免介面過於臃腫
意義:客戶端不應該依賴它不需要的接口,一個類別對另一個類別的依賴應該建立在最小的接口上。
解決:適度細化接口,將臃腫的接口拆分為獨立的幾個接口。
範例:考試接口,包含考語數外、理化生、政史地等方法。學生類,實現考試接口,參加考試。文科生類、理科生類派生自學生類,實現考試接口時,就都需要實現一些自己不需要的方法(因為文科生不考理化生、理科生不考政史地)。這時,需要對考試介面進行細化,分為基礎科考試介面、文科考試介面及理科考試介面;學生類別實作基礎科考試介面;文科生、理科生另外各自實現文科考試介面、理科考試介面。
5.迪米特法則(Demeter Principle)目的:降低類別與類別之間的耦合
意義:每一個軟體單位對其他的單位都只有最少的知識,而且局限於那些與本單位密切相關的軟體單位。
解決:不發生依賴、關聯、組合、聚合等耦合關係的陌生類別不要作為局部變數的形式出現在類別的內部。
範例:校長管理老師,老師管理學生。當校長需要全體點名時,首先對老師點名,但是不必通過老師獲取學生的信息並點名,而應該讓老師對各自管理學生的點名,否則校長和學生之間就發生了原本不必要的耦合,這樣當學生類發生變化時,既要修改老師類,也要修改校長類。
#目的:防止類別的系統龐大
意義:當要擴展類別的功能時,優先考慮使用合成/聚合,而不是繼承。
解決:當類別與類別之間的關係是"Is-A"時,用繼承;當類別與類別之間的關係是"Has-A"時,用組合。
實例:如橋接模式,抽象和實作可以獨立的變化,擴展功能時,增加實現類別即可;例如裝飾模式,只需要一個類,即可為一類類別擴充新功能。對於顯示圖形需求,用圖形Shape類,和顯示Paint類實作。每個Shape類別有一個Paint類別指標負責圖形繪製顯示。 Paint類別衍生RedPaint類和BluePaint類,傳遞給Shape類,實作圖形不同顏色繪製,讓圖形繪製邏輯和圖形繪製實作可獨立變化。某天增加需求,所有的繪製需要加邊框,可增加PaintDecorator類,派生自Paint基類,每一個PaintDecorator類有一個Paint對象指針,增加虛函數AddedPaint,重寫Paint的繪製方法,增加AddedPaint方法的調用。增加BorderPaintDecorator類,衍生自PaintDecorator類,重寫AddedPaint方法,增加加入繪製邊框程式碼。這樣新增加一個類別可以擴充原始所有畫筆類別的功能。
目的:提高擴充性、便於維護
意義:對擴展開放,對修改封閉。即係統進行擴充是被鼓勵的,對現有系統程式碼進行修改是不被支援的。也就是說,當軟體有新的需求變化的時候,只需要透過對軟體框架進行擴展來適應新的需求,而不是對框架內部的程式碼進行修改。
解決:設計模式前面6大原則以及23種設計模式遵循的好,開閉原則自然遵守的好。對需求的變更保持前瞻性和預見性,就可以使抽象具有更廣泛適用性,設計出的軟體架構就能相對穩定。軟體需求中易變的細節,透過從抽象派生出實作類別來擴展。
#include <iostream> //绘制类 class Paint { public: virtual void Draw() = 0; }; //红色绘制类 class RedPaint : public Paint { public: void Draw() { std::cout << "Color Red!" << std::endl; } }; //蓝色绘制类 class BluePaint : public Paint { public: void Draw() { std::cout << "Color Blue!" << std::endl; } }; //图形类,使用桥接模式,将图形绘制逻辑与绘制实现解耦 class Shape { public: Shape(Paint* pt) : m_pPt(pt) { } virtual void Show() { std::cout << "Shape Style:" << std::endl; m_pPt->Draw(); } protected: Paint* m_pPt; }; //长方形类 class Rectangle : public Shape { public: Rectangle(Paint* pt) : Shape(pt) { } }; //圆形类 class Circle : public Shape { public: Circle(Paint* pt) : Shape(pt) { } }; //附加绘制类,使用装饰模式,对原有绘制类进行功能扩展 class PaintDecorator : public Paint { public: PaintDecorator(Paint* pt) : m_pPt(pt) { } void Draw() { m_pPt->Draw(); AddedPaint(); } virtual void AddedPaint() = 0; protected: Paint* m_pPt; }; //附加边框类,对绘制类添加边框绘制功能 class BoarderDecorator : public PaintDecorator { public: BoarderDecorator(Paint* pt) : PaintDecorator(pt) { } void AddedPaint() { std::cout << "With Boarder!" << std::endl; } }; void main() { Shape* pShape = new Circle(new BoarderDecorator(new RedPaint())); pShape->Show(); return; }
相關推薦:
##JavaScript設計模式之工廠模式和構造器模式_javascript技巧
以上是設計模式七大原則總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!