1. 程式人生 > >設計模式七大原則總結

設計模式七大原則總結

1.單一職責原則(Single Responsibility Principle)

目的:降低程式碼複雜度、系統解耦合、提高可讀性

含義:對於一個類,只有一個引起該類變化的原因;該類的職責是唯一的,且這個職責是唯一引起其他類變化的原因。

解決:將不同的職責封裝到不同的類或者模組中。 當有新的需求將現有的職責分為顆粒度更小的職責的時候,應該及時對現有程式碼進行重構。當系統邏輯足夠簡單,方法足夠少,子類夠少或後續關聯夠少時,也可以不必嚴格遵循你SRP原則,避免過度設計、顆粒化過於嚴重。

例項:電線類Wire為居民供電,電壓為220v;但是新的需求增加,電線也輸送高壓電,電壓為200kv,原有電線類可以增加方法實現擴充,這就違背了單一職責原則。可以提供基類,建立兩個派生類,居民供電線、高壓輸電線。

2.里氏代換原則(Liskov Substitution Principle)

目的:避免系統繼承體系被破壞

含義:所有引用基類的地方必須能透明地使用其子類的物件。

解決:子類可以實現父類的抽象方法,但是不能覆蓋父類的非抽象方法;子類中可以增加自己特有的方法;當子類覆蓋或實現父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入引數更寬鬆;當子類的方法實現父類的抽象方法時,方法的後置條件(即方法的返回值)要比父類更嚴格。如果子類不能完整地實現父類的方法,或者父類的一些方法在子類中已經發生畸變,則建議斷開繼承關係,採用依賴,聚合,組合等關係代替繼承。

例項:已經定義鳥類具有兩個翅膀飛的方法;新加入鴕鳥,不會飛,如果覆蓋父類的方法,在兩個翅膀飛的方法中什麼也不做,就違背里氏替換原則,導致所有鳥都不會飛。應該建立並列的兩種鳥基類,會飛與不會飛的。前置條件更寬鬆、後置條件更嚴格,比如父類返回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)

目的:降低類與類之間的耦合

含義:每一個軟體單位對其他的單位都只有最少的知識,而且侷限於那些與本單位密切相關的軟體單位。

解決:不發生依賴、關聯、組合、聚合等耦合關係的陌生類不要作為區域性變數的形式出現在類的內部。

例項:校長管理老師,老師管理學生。校長需要全體點名時,首先對老師點名,但是不必通過老師獲取學生的資訊並點名,而應該讓老師對各自管理學生的點名,否則校長和學生之間就發生了原本不必要的耦合,這樣當學生類發生變化時,既要修改老師類,也要修改校長類。

6.合成複用原則(Composite Reuse Principle)

目的:防止類的體系龐大

含義:當要擴充套件類的功能時,優先考慮使用合成/聚合,而不是繼承。

解決:當類與類之間的關係是"Is-A"時,用繼承;當類與類之間的關係是"Has-A"時,用組合。

例項:如橋接模式,抽象和實現可以獨立的變化,擴充套件功能時,增加實現類即可;比如裝飾模式,只需要一個類,即可為一類類擴充套件新功能。對於顯示圖形需求,用圖形Shape類,和顯示Paint類實現。每個Shape類有一個Paint類指標負責圖形繪製顯示。Paint類派生RedPaint類和BluePaint類,傳遞給Shape類,實現圖形不同顏色繪製,這樣圖形繪製邏輯和圖形繪製實現可獨立變化。某天增加需求,所有的繪製需要加邊框,可增加PaintDecorator類,派生自Paint基類,每一個PaintDecorator類有一個Paint物件指標,增加虛擬函式AddedPaint,重寫Paint的繪製方法,增加AddedPaint方法的呼叫。增加BorderPaintDecorator類,派生自PaintDecorator類,重寫AddedPaint方法,增加新增繪製邊框程式碼。這樣新增加一個類可以對原始所有畫筆類的功能進行擴充。

7.開閉原則(Open Close Principle)

目的:提高擴充套件性、便於維護

含義:對擴充套件開放,對修改封閉。即系統進行擴充套件是被鼓勵的,對現有系統程式碼進行修改是不被支援的。也就是說,當軟體有新的需求變化的時候,只需要通過對軟體框架進行擴充套件來適應新的需求,而不是對框架內部的程式碼進行修改。

解決:設計模式前面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 BorderDecorator : public PaintDecorator
{
public:
	BorderDecorator(Paint* pt) : PaintDecorator(pt)
	{
	}

	void AddedPaint()
	{
		std::cout << "With Border!" << std::endl;
	}
};

void main()
{
	Shape* pShape = new Circle(new BorderDecorator(new RedPaint()));
	pShape->Show();

	return;
}