Bridge模式又稱為Handle/Body模式。
在軟件系統中,經常面臨著“某些結構復雜的對象”的創建工作,由于需求的變化,這些對象經常面臨著劇烈的變化,但是他們卻擁有比較穩定一致的接口。大部分創建型模式,就是為了解決如何向“客戶程序”隔離出“這些易變對象”,從而使得“依賴這些易變對象的客戶程序”不隨著需求的改變而改變。
事實上,上面的假設是經常面臨劇烈變化的對象(實現細節b)擁有比較穩定一致的接口(抽象B)。現在的問題是,如果抽象B由于一些固有的原因也是面臨著劇烈變化,那應該怎么辦?
業務舉例:
假如我們需要開發一個同時支持PC和手機的坦克游戲,游戲在PC和手機上的功能都一樣,都有同樣的類型,面臨同樣的功能需求變化,坦克有不同的型號:T50、T75和T90。(上面所言的抽象B就是坦克,現在抽象B即坦克本身也要變化了,需要PC上的坦克和手機上的坦克)
對于其中的坦克設計,我們可能很容易設計出來一個Tank的抽象基類,然后各種不同型號的Tank繼承該基類:
// 抽象的坦克
class Tank
{
public:
virtual void shot() = 0; // 射擊
virtual void run() = 0; // 行進
virtual void turn() = 0; // 轉向
};
// 不同型號的坦克
class T50 : public Tank
{
...
};
class T75 : public Tank
{
...
};
class T90 : public Tank
{
...
};
由于PC機上和手機的圖形繪制、聲效、操作等具體實現有很大的差別,因此,對于各種型號的坦克,都要提供各種不同平臺上的實現:
// PC機上的各型號坦克的實現
class PCT50 : public T50
{
...
};
class PCT75 : public T75
{
...
};
class PCT90 : public T90
{
...
};
// 手機上的各型號坦克的實現
class MobileT50 : public T50
{
...
};
class MobileT75 : public T75
{
...
};
class MobileT90 : public T90
{
...
};
這樣以來就有會產生如上6個leaf或者terminal 類。
上面的設計思路會帶來很多問題:有很多重復的代碼,類的結果過于復雜,難以維護,以至于引入任何新的平臺,比如TV上的Tank游戲,就會產生9個leaf或terminal類,顯然會讓整個類層級結構劇烈復雜化起來。
上述問題的結癥:Tank類具有兩個變化的維度,即“型號的變化”和“平臺的變化”,Bridge設計模式就是利用面向對象的技術來使得Tank類型可以輕松地沿著“型號”和“平臺”兩個方向變化,而不引入額外的復雜度。
“Decouple an abstraction from its implementation so that the two can vary independently.” – GoF
下面是Bridge設計模式的類圖:
另外一個被廣泛用來說明Bridge設計模式的例子,就是GoF原著《Design Patterns, Elements of Reusable Object-Oriented Software》中講到的在不同的圖形系統下繪圖的情況。
下面是C++實現代碼示例:
// Bridge.h
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class PlatformImplementor;
class Tank
{
protected:
auto_ptr<PlatformImplementor> pli;
public:
Tank(auto_ptr<PlatformImplementor> plim) // plim不能與pli相同,因為如果相同,那么pli = plim;就必須寫成:
{ // this->pli = pli;
pli = plim; // 而這會導致pli不知所指,這或許算是VS2005的一個bug
}
virtual string shot() = 0;
virtual string run() = 0;
virtual string turn() = 0;
public:
virtual ~Tank()
{
cout << "in the destructor of Tank..." << endl;
}
};
class PlatformImplementor
{
public:
virtual string draw_tank() = 0;
virtual string move_tank() = 0;
virtual string do_shot() = 0;
virtual string turn() = 0;
public:
virtual ~PlatformImplementor()
{
cout << "in the destructor of PlatformImplementor..." << endl;
}
};
// -------------------------------
class T50 : public Tank
{
public:
T50(auto_ptr<PlatformImplementor> plim) : Tank(plim)
{
string tmp_str = pli->draw_tank();
cout << "T50 - " << tmp_str << endl;
}
string run()
{
return "T50 - " + pli->move_tank();
}
string shot()
{
return "T50 - " + pli->do_shot();
}
string turn()
{
return "T50 - " + pli->turn();
}
~T50()
{
cout << "in the destructor of T50..." << endl;
}
};
class T75 : public Tank
{
public:
T75(auto_ptr<PlatformImplementor> plim) : Tank(plim)
{
string tmp_str = pli->draw_tank();
cout << "T75 - " << tmp_str << endl;
}
string run()
{
return "T75 - " + pli->move_tank();
}
string shot()
{
return "T75 - " + pli->do_shot();
}
string turn()
{
return "T75 - " + pli->turn();
}
~T75()
{
cout << "in the destructor of T75..." << endl;
}
};
class T90 : public Tank
{
public:
T90(auto_ptr<PlatformImplementor> plim) : Tank(plim)
{
string tmp_str = pli->draw_tank();
cout << "T90 - " << tmp_str << endl;
}
string run()
{
return "T90 - " + pli->move_tank();
}
string shot()
{
return "T90 - " + pli->do_shot();
}
string turn()
{
return "T90 - " + pli->turn();
}
~T90()
{
cout << "in the destructor of T90..." << endl;
}
};
// --------------------------
class PCPlatformImplementor : public PlatformImplementor
{
public:
string draw_tank()
{
return "PC platform: Draw a tank"; // 假定在這里畫坦克
}
string move_tank()
{
return "PC platform: Move a tank"; // 假定在這里移動坦克
}
string do_shot()
{
return "PC platform: Fire the target"; // 假定在這里坦克開火
}
string turn()
{
return "PC platform: Turn direction"; // 假定在這里坦克轉彎
}
public:
~PCPlatformImplementor()
{
cout << "in the destructor of PCPlatformImplementor..." << endl;
}
};
class MobilePlatformImplementor : public PlatformImplementor
{
public:
string draw_tank()
{
return "Mobile platform: Draw a tank";
}
string move_tank()
{
return "Mobile platform: Move a tank";
}
string do_shot()
{
return "Mobile platform: Fire the target";
}
string turn()
{
return "Mobile platform: Turn direction";
}
public:
~MobilePlatformImplementor()
{
cout << "in the destructor of MobilePlatformImplementor..." << endl;
}
};
// 測試代碼:Bridge.cpp
#include "Bridge.h"
int main(int argc, char **argv)
{
auto_ptr<PlatformImplementor> pc_pli1(new PCPlatformImplementor);
T50 *pc_T50 = new T50(pc_pli1);
cout << pc_T50->turn() << endl;
cout << pc_T50->shot() << endl;
cout << pc_T50->turn() << endl;
// 由于auto_ptr的特性,pc_pli1到此已經無所指向,詳見Tank類的構造函數
delete pc_T50;
cout << "--------------------------------------" << endl;
auto_ptr<PlatformImplementor> pc_pli2(new PCPlatformImplementor);
T75 *pc_T75 = new T75(pc_pli2);
cout << pc_T75->turn() << endl;
cout << pc_T75->shot() << endl;
cout << pc_T75->turn() << endl;
// 由于auto_ptr的特性,pc_pli2到此已經無所指向,詳見Tank類的構造函數
delete pc_T75;
cout << "--------------------------------------" << endl;
auto_ptr<PlatformImplementor> pc_pli3(new PCPlatformImplementor);
T90 *pc_T90 = new T90(pc_pli3);
cout << pc_T90->turn() << endl;
cout << pc_T90->shot() << endl;
cout << pc_T90->turn() << endl;
// 由于auto_ptr的特性,pc_pli3到此已經無所指向,詳見Tank類的構造函數
delete pc_T90;
cout << "--------------------------------------" << endl;
auto_ptr<PlatformImplementor> mo_pli1(new MobilePlatformImplementor);
T50 *mo_T50 = new T50(mo_pli1);
cout << mo_T50->turn() << endl;
cout << mo_T50->shot() << endl;
cout << mo_T50->turn() << endl;
// 由于auto_ptr的特性,mo_pli1到此已經無所指向
delete mo_T50;
cout << "--------------------------------------" << endl;
auto_ptr<PlatformImplementor> mo_pli2(new MobilePlatformImplementor);
T75 *mo_T75 = new T75(mo_pli2);
cout << mo_T75->turn() << endl;
cout << mo_T75->shot() << endl;
cout << mo_T75->turn() << endl;
// 由于auto_ptr的特性,mo_pli2到此已經無所指向
delete mo_T75;
cout << "--------------------------------------" << endl;
auto_ptr<PlatformImplementor> mo_pli3(new MobilePlatformImplementor);
T90 *mo_T90 = new T90(mo_pli3);
cout << mo_T90->turn() << endl;
cout << mo_T90->shot() << endl;
cout << mo_T90->turn() << endl;
// 由于auto_ptr的特性,mo_pli3到此已經無所指向,詳見Tank類的構造函數
delete mo_T90;
return 0;
}
運行結果:
T50 - PC platform: Draw a tank
T50 - PC platform: Turn direction
T50 - PC platform: Fire the target
T50 - PC platform: Turn direction
in the destructor of T50...
in the destructor of Tank...
in the destructor of PCPlatformImplementor...
in the destructor of PlatformImplementor...
--------------------------------------
T75 - PC platform: Draw a tank
T75 - PC platform: Turn direction
T75 - PC platform: Fire the target
T75 - PC platform: Turn direction
in the destructor of T75...
in the destructor of Tank...
in the destructor of PCPlatformImplementor...
in the destructor of PlatformImplementor...
--------------------------------------
T90 - PC platform: Draw a tank
T90 - PC platform: Turn direction
T90 - PC platform: Fire the target
T90 - PC platform: Turn direction
in the destructor of T90...
in the destructor of Tank...
in the destructor of PCPlatformImplementor...
in the destructor of PlatformImplementor...
--------------------------------------
T50 - Mobile platform: Draw a tank
T50 - Mobile platform: Turn direction
T50 - Mobile platform: Fire the target
T50 - Mobile platform: Turn direction
in the destructor of T50...
in the destructor of Tank...
in the destructor of MobilePlatformImplementor...
in the destructor of PlatformImplementor...
--------------------------------------
T75 - Mobile platform: Draw a tank
T75 - Mobile platform: Turn direction
T75 - Mobile platform: Fire the target
T75 - Mobile platform: Turn direction
in the destructor of T75...
in the destructor of Tank...
in the destructor of MobilePlatformImplementor...
in the destructor of PlatformImplementor...
--------------------------------------
T90 - Mobile platform: Draw a tank
T90 - Mobile platform: Turn direction
T90 - Mobile platform: Fire the target
T90 - Mobile platform: Turn direction
in the destructor of T90...
in the destructor of Tank...
in the destructor of MobilePlatformImplementor...
in the destructor of PlatformImplementor...
上述實現代碼中各個類和Bridge模式中的各個類之間的對應關系:
Tank < ------ > Abstraction
T50、T75和T90 < ------ > RefinedAbstraction
PlatformImplementor < ------ > Implementor
PCPlatformImplementor < ------ > ConcreteImplementorA
MobilePlatformImplementor < ------ > ConcreteImplementorB
前面講到的Adapter模式(對象適配器形式),從UML的角度來看,也可以畫成:
因為Adapter中包含了一個Adaptee對象,這是一個聚合或者組合的關系。而且也是在Adapter的request方法中調用了Adaptee對象中的方法,從這個角度而言,Adapter模式和Bridge模式是非常類似的。
但是,他們之間有本質的區別:
1. 在Adapter模式中,Adaptee本身往往已經是一個具體的、已經存在的類。在Bridge模式中,Implementor則是一個抽象類或者接口;
2. 在Adapter模式中,Adapter類也是一個具體的類。在Bridge模式中,Abstraction則是一個抽象類;
3. 在Adapter模式中,Adapter類派生于一個抽象類/接口(客戶程序所期望的)。在Bridge模式中,Abstraction類則不存在這樣的情況。
4. 最本質同時也是最重要的區別是,它們的意圖是不同的。