設計模式 - Bridge 橋模式

朱果果發表於2021-10-29

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++設計模式——橋接模式

 

相關文章