Bridge橋模式也屬於”的單一職責“模式中的典型模式。
問題描述:
我們繪製圖形時,圖形可以有不同形狀以及不同顏色,比如圓形可以是紅的,綠的,方形可以是紅的綠的,如果用程式碼來描繪這些類,會有如下:
1 class Shape{ 2 }; 3 class Rectangle : public Shape{ 4 }; 5 class Circle : public Shape{ 6 }; 7 class Color{ 8 }; 9 class Red : public Color{ 10 }; 11 class Blue : public Color{ 12 }; 13 class RedRectangle : public Red{ 14 }; 15 class BlueRectangle : public Blue{ 16 };
每增加一種圖形或者顏色,新增的類就會成倍得增長。而且CRedRectangle繼承於顏色,似乎也不太合理,CRedRectangle和CRed之間不是一種is-a的關係。下面通過橋模式改善它?
定義
將抽象部分(業務功能)與實現部分(平臺實現)分離,使它們都可以獨立地變化。 ——《設計模式》GoF
簡單的說就是抽象對外提供的介面,對外隱瞞實現部分,在抽象中引用實現部分,從而實現抽象對實現部分的呼叫,而抽象中引用的實現部分可以在今後的開發中,切換為別的實現部分。
動機
- 解決繼承帶來的問題
物件的繼承關係是在編譯時就定義好的,無法在執行時改變從父類繼承的實現。父類實現中的任何變化都必然會導致子類發生變化。比如上面的程式碼,Red類是Color抽象類的具體實現,RedRectangle從Red類中繼承了紅色屬性,就和顏色的實現繫結在了一起,RedRectangle的顏色實現就難以修改或擴充套件。通過橋接模式把依賴具體實現,提升為依賴抽象,來完成物件和變化因素之間的低耦合,提高系統的可維護性和擴充套件性。橋接模式的主要目的是將一個物件的變化與其它變化隔離開,讓彼此之間的耦合度最低。 - 合成/聚合複用原則
聚合表示一種弱的‘擁有’關係,體現的是A物件可以包含B物件,但B物件不是A物件的一部分;合成則是一種強的‘擁有’關係,體現了嚴格的部分和整體的關係,部分和整體的生命週期一樣。
繪製矩形、圓形、橢圓、正方形,我們至少需要4個形狀類,但是如果繪製的圖形需要具有不同的顏色,如紅色、綠色、藍色等,此時至少有如下兩種設計方案:
方案一:為每一種形狀都提供一套顏色版本
方案二:根據實際需要對形狀和顏色進行組合
明顯方案二採用聚合的方式可以減少很多類的數量。
UML類圖
Abstraction類定義了抽象類的介面,並且維護一個指向Implementor實現類的指標;
RefineAbstraction類擴充了Abstraction類的介面;
Implementor類定義了實現類的介面,這個介面不一定要與Abstraction的介面完全一致;實際上,這兩個介面可以完全不同;
ConcreteImplementor類實現了Implementor定義的介面。
程式碼實現
1 #include <iostream> 2 3 using namespace std; 4 5 //具體實現的抽象 6 class Implementor { 7 public: 8 virtual void operatonImpl() = 0; 9 }; 10 11 //具體實現 12 class ConcreteImplementor : public Implementor { 13 public: 14 void oerationImpl() { cout << "OperationImpl" << endl; } 15 }; 16 17 class Abstruction { 18 public: 19 Abstruction(Implementor* pImpl) : m_pImpl(pImpl) {} 20 virtual void operation() = 0; 21 22 protected: 23 Implementor* m_pImpl; 24 }; 25 26 //重新定義抽象 27 class RedfinedAbstraction : public Abstruction { 28 public: 29 RedfineAbstraction(Implementor* pImpl) : Abstruction(pImpl) {} 30 void operation() { m_pImpl->operatonImpl(); } 31 }; 32 33 int main() { 34 Implementor* pImplObj = new ConcreteImplementor(); 35 Abstruction* pAbsObj = new RedfineAbstraction(pImplObj); 36 pAbsObj->operation(); 37 delete pImplObj; 38 pImplObj = nullptr; 39 delete pAbsObj; 40 pAbsObj = nullptr; 41 return 0; 42 }
使用橋模式重新實現形狀與顏色的程式碼:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Color { 7 public: 8 virtual string name() = 0; 9 10 protected: 11 string mName; 12 }; 13 14 class Green : public Color { 15 public: 16 Green() { mName = "Green"; } 17 virtual ~Green() {} 18 virtual string name() { return mName; } 19 }; 20 21 class Red : public Color { 22 public: 23 Red() { mName = "Red"; } 24 virtual ~Red() {} 25 virtual string name() { return mName; } 26 }; 27 28 class Shape { 29 public: 30 Shape(Color* color) : mColor(color) {} 31 virtual void myShape() = 0; 32 33 protected: 34 Color* mColor; 35 }; 36 37 class Rectangle : public Shape { 38 public: 39 Rectangle(Color* color) : Shape(color) {} 40 virtual void myShape() { 41 cout << "Rectangle has a " << mColor->name() << " color\n"; 42 } 43 }; 44 45 class Circle : public Shape { 46 public: 47 Circle(Color* color) : Shape(color) {} 48 virtual void myShape() { 49 cout << "Circle has a " << mColor->name() << " color\n"; 50 } 51 }; 52 53 int main() { 54 Color* red = new Red(); 55 Color* green = new Green(); 56 57 Shape* rectangle = new Rectangle(red); 58 rectangle->myShape(); 59 Shape* circle = new Circle(green); 60 circle->myShape(); 61 62 return 0; 63 }
橋模式的優點
分離抽象介面及其實現的部分
- 橋接模式有點類似於多繼承,但是多繼承違背了類的單一職責原則(一個類只有一個變化的原因),複用性差,且多繼承結構中類的個數龐大,橋接模式是比多繼承方案更好的解決方案。
- 橋接模式提高了系統的可擴充性,在兩個變化維度中任意擴充套件一個維度,都不需要修改原系統。
- 實現細節對客戶透明,可以對使用者隱藏實現細節。
橋模式的缺點
- 橋接模式的引入會增加系統的理解與設計難度,由於聚合關聯關係建立在抽象層,要求開發者針對抽象進行設計與程式設計。
- 橋接模式要求正確識別出系統中兩個獨立變化的維度,因此其使用範圍有侷限性。
參考:
橋接模式(c++實現)
C++設計模式——橋接模式