C++設(shè)計(jì)模式編程中使用Bridge橋接模式的完全攻略
橋接模式將抽象(Abstraction)與實(shí)現(xiàn)(Implementation)分離,使得二者可以獨(dú)立地變化。
橋接模式典型的結(jié)構(gòu)圖為:
在橋接模式的結(jié)構(gòu)圖中可以看到,系統(tǒng)被分為兩個相對獨(dú)立的部分,左邊是抽象部分,右邊是實(shí)現(xiàn)部分,這兩個部分可以互相獨(dú)立地進(jìn)行修改:例如上面問題中的客戶需求變化,當(dāng)用戶需求需要從 Abstraction 派生一個具體子類時候,并不需要像上面通過繼承方式實(shí)現(xiàn)時候需要添加子類 A1 和 A2 了。另外當(dāng)上面問題中由于算法添加也只用改變右邊實(shí)現(xiàn)(添加一個具體化子類),而右邊不用在變化,也不用添加具體子類了。
一切都變得 elegant!
橋接模式號稱設(shè)計(jì)模式中最難理解的模式之一,關(guān)鍵就是這個抽象和實(shí)現(xiàn)的分離非常讓人奇怪,大部分人剛看到這個定義的時候都會認(rèn)為實(shí)現(xiàn)就是繼承自抽象,那怎么可能將他們分離呢。
《大話設(shè)計(jì)模式》中就Bridge模式的解釋:
手機(jī)品牌和軟件是兩個概念,不同的軟件可以在不同的手機(jī)上,不同的手機(jī)可以有相同的軟件,兩者都具有很大的變動性。如果我們單獨(dú)以手機(jī)品牌或手機(jī)軟件為基類來進(jìn)行繼承擴(kuò)展的話,無疑會使類的數(shù)目劇增并且耦合性很高,(如果更改品牌或增加軟件都會增加很多的變動)兩種方式的結(jié)構(gòu)如下:
所以將兩者抽象出來兩個基類分別是PhoneBrand和PhoneSoft,那么在品牌類中聚合一個軟件對象的基類將解決軟件和手機(jī)擴(kuò)展混亂的問題,這樣兩者的擴(kuò)展就相對靈活,剪短了兩者的必要聯(lián)系,結(jié)構(gòu)圖如下:
這樣擴(kuò)展品牌和軟件就相對靈活獨(dú)立,達(dá)到解耦的目的!
抽象基類及接口:
1、Abstraction::Operation():定義要實(shí)現(xiàn)的操作接口
2、AbstractionImplement::Operation():實(shí)現(xiàn)抽象類Abstaction所定義操作的接口,由其具體派生類ConcreteImplemenA、ConcreteImplemenA或者其他派生類實(shí)現(xiàn)。
3、在Abstraction::Operation()中根據(jù)不同的指針多態(tài)調(diào)用AbstractionImplement::Operation()函數(shù)。
理解:
Bridge用于將表示和實(shí)現(xiàn)解耦,兩者可以獨(dú)立的變化.在Abstraction類中維護(hù)一個AbstractionImplement類指針,需要采用不同的實(shí)現(xiàn)方式的時候只需要傳入不同的AbstractionImplement派生類就可以了.
Bridge的實(shí)現(xiàn)方式其實(shí)和Builde十分的相近,可以這么說:本質(zhì)上是一樣的,只是封裝的東西不一樣罷了.兩者的實(shí)現(xiàn)都有如下的共同點(diǎn):
抽象出來一個基類,這個基類里面定義了共有的一些行為,形成接口函數(shù)(對接口編程而不是對實(shí)現(xiàn)編程),這個接口函數(shù)在Buildier中是BuildePart函數(shù)在Bridge中是Operation函數(shù);
其次,聚合一個基類的指針,如Builder模式中Director類聚合了一個Builder基類的指針,而Brige模式中Abstraction類聚合了一個AbstractionImplement基類的指針(優(yōu)先采用聚合而不是繼承);
而在使用的時候,都把對這個類的使用封裝在一個函數(shù)中,在Bridge中是封裝在Director::Construct函數(shù)中,因?yàn)檠b配不同部分的過程是一致的,而在Bridge模式中則是封裝在Abstraction::Operation函數(shù)中,在這個函數(shù)中調(diào)用對應(yīng)的AbstractionImplement::Operation函數(shù).就兩個模式而言,Builder封裝了不同的生成組成部分的方式,而Bridge封裝了不同的實(shí)現(xiàn)方式.
橋接模式就將實(shí)現(xiàn)與抽象分離開來,使得RefinedAbstraction依賴于抽象的實(shí)現(xiàn),這樣實(shí)現(xiàn)了依賴倒轉(zhuǎn)原則,而不管左邊的抽象如何變化,只要實(shí)現(xiàn)方法不變,右邊的具體實(shí)現(xiàn)就不需要修改,而右邊的具體實(shí)現(xiàn)方法發(fā)生變化,只要接口不變,左邊的抽象也不需要修改。
優(yōu)點(diǎn)
1.將實(shí)現(xiàn)抽離出來,再實(shí)現(xiàn)抽象,使得對象的具體實(shí)現(xiàn)依賴于抽象,滿足了依賴倒轉(zhuǎn)原則。
2.將可以共享的變化部分,抽離出來,減少了代碼的重復(fù)信息。
3.對象的具體實(shí)現(xiàn)可以更加靈活,可以滿足多個因素變化的要求。
缺點(diǎn)
客戶必須知道選擇哪一種類型的實(shí)現(xiàn)。
設(shè)計(jì)中有超過一維的變化我們就可以用橋模式。如果只有一維在變化,那么我們用繼承就可以圓滿的解決問題。
代碼示例:
Abstraction.h
#ifndef _ABSTRACTION_H_ #define _ABSTRACTION_H_ class AbstractionImplement; class Abstraction { public: virtual void Operation()=0;//定義接口,表示該類所支持的操作 virtual ~Abstraction(); protected: Abstraction(); }; class RefinedAbstractionA:public Abstraction { public: RefinedAbstractionA(AbstractionImplement* imp);//構(gòu)造函數(shù) virtual void Operation();//實(shí)現(xiàn)接口 virtual ~RefinedAbstractionA();//析構(gòu)函數(shù) private: AbstractionImplement* _imp;//私有成員 }; class RefinedAbstractionB:public Abstraction { public: RefinedAbstractionB(AbstractionImplement* imp);//構(gòu)造函數(shù) virtual void Operation();//實(shí)現(xiàn)接口 virtual ~RefinedAbstractionB();//析構(gòu)函數(shù) private: AbstractionImplement* _imp;//私有成員 }; #endif Abstraction.cpp #include "Abstraction.h" #include "AbstractionImplement.h" #include <iostream> using namespace std; Abstraction::Abstraction() {} Abstraction::~Abstraction() {} RefinedAbstractionA::RefinedAbstractionA(AbstractionImplement* imp) { this->_imp = imp; } RefinedAbstractionA::~RefinedAbstractionA() { delete this->_imp; this->_imp = NULL; } void RefinedAbstractionA::Operation() { cout << "RefinedAbstractionA::Operation" << endl; this->_imp->Operation(); } RefinedAbstractionB::RefinedAbstractionB(AbstractionImplement* imp) { this->_imp = imp; } RefinedAbstractionB::~RefinedAbstractionB() { delete this->_imp; this->_imp = NULL; } void RefinedAbstractionB::Operation() { cout << "RefinedAbstractionB::Operation" << endl; this->_imp->Operation(); } AbstractImplement.h #ifndef _ABSTRACTIONIMPLEMENT_H_ #define _ABSTRACTIONIMPLEMENT_H_ //抽象基類,定義了實(shí)現(xiàn)的接口 class AbstractionImplement { public: virtual void Operation()=0;//定義操作接口 virtual ~AbstractionImplement(); protected: AbstractionImplement(); }; // 繼承自AbstractionImplement,是AbstractionImplement的不同實(shí)現(xiàn)之一 class ConcreteAbstractionImplementA:public AbstractionImplement { public: ConcreteAbstractionImplementA(); void Operation();//實(shí)現(xiàn)操作 ~ConcreteAbstractionImplementA(); protected: }; // 繼承自AbstractionImplement,是AbstractionImplement的不同實(shí)現(xiàn)之一 class ConcreteAbstractionImplementB:public AbstractionImplement { public: ConcreteAbstractionImplementB(); void Operation();//實(shí)現(xiàn)操作 ~ConcreteAbstractionImplementB(); protected: }; #endif AbstractImplement.cpp #include "AbstractionImplement.h" #include <iostream> using namespace std; AbstractionImplement::AbstractionImplement() {} AbstractionImplement::~AbstractionImplement() {} ConcreteAbstractionImplementA::ConcreteAbstractionImplementA() {} ConcreteAbstractionImplementA::~ConcreteAbstractionImplementA() {} void ConcreteAbstractionImplementA::Operation() { cout << "ConcreteAbstractionImplementA Operation" << endl; } ConcreteAbstractionImplementB::ConcreteAbstractionImplementB() {} ConcreteAbstractionImplementB::~ConcreteAbstractionImplementB() {} void ConcreteAbstractionImplementB::Operation() { cout << "ConcreteAbstractionImplementB Operation" << endl; }
main.cpp
#include "Abstraction.h" #include "AbstractionImplement.h" #include <iostream> using namespace std; int main() { /* 將抽象部分與它的實(shí)現(xiàn)部分分離,使得它們可以獨(dú)立地變化 1、抽象Abstraction與實(shí)現(xiàn)AbstractionImplement分離; 2、抽象部分Abstraction可以變化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp2); 3、實(shí)現(xiàn)部分AbstractionImplement也可以變化,如new ConcreteAbstractionImplementA()、new ConcreteAbstractionImplementB(); */ AbstractionImplement* imp = new ConcreteAbstractionImplementA(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementA Abstraction* abs = new RefinedAbstractionA(imp); //抽象部分RefinedAbstractionA abs->Operation(); cout << "-----------------------------------------" << endl; AbstractionImplement* imp1 = new ConcreteAbstractionImplementB(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementB Abstraction* abs1 = new RefinedAbstractionA(imp1); //抽象部分RefinedAbstractionA abs1->Operation(); cout << "-----------------------------------------" << endl; AbstractionImplement* imp2 = new ConcreteAbstractionImplementA(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementA Abstraction* abs2 = new RefinedAbstractionB(imp2); //抽象部分RefinedAbstractionB abs2->Operation(); cout << "-----------------------------------------" << endl; AbstractionImplement* imp3 = new ConcreteAbstractionImplementB(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementB Abstraction* abs3 = new RefinedAbstractionB(imp3); //抽象部分RefinedAbstractionB abs3->Operation(); cout << endl; return 0; }
代碼說明:
Bridge模式將抽象和實(shí)現(xiàn)分別獨(dú)立實(shí)現(xiàn),在代碼中就是Abstraction類和AbstractionImplement類。
使用組合(委托)的方式將抽象和實(shí)現(xiàn)徹底地解耦,這樣的好處是抽象和實(shí)現(xiàn)可以分別獨(dú)立地變化,系統(tǒng)的耦合性也得到了很好的降低。
GoF的那句話中的“實(shí)現(xiàn)”該怎么去理解:“實(shí)現(xiàn)”特別是和“抽象”放在一起的時候我們“默認(rèn)”的理解是“實(shí)現(xiàn)”就是“抽象”的具體子類的實(shí)現(xiàn),但是這里GoF所謂的“實(shí)現(xiàn)”的含義不是指抽象基類的具體子類對抽象基類中虛函數(shù)(接口)的實(shí)現(xiàn),是和繼承結(jié)合在一起的。而這里的“實(shí)現(xiàn)”的含義指的是怎么去實(shí)現(xiàn)用戶的需求,并且指的是通過組合(委托)的方式實(shí)現(xiàn)的,因此這里的實(shí)現(xiàn)不是指的繼承基類、實(shí)現(xiàn)基類接口,而是指的是通過對象組合實(shí)現(xiàn)用戶的需求。
實(shí)際上上面使用Bridge模式和使用帶來問題方式的解決方案的根本區(qū)別在于是通過繼承還是通過組合的方式去實(shí)現(xiàn)一個功能需求。
備注:
由于實(shí)現(xiàn)的方式有多種,橋接模式的核心就是把這些實(shí)現(xiàn)獨(dú)立出來,讓他們各自變化。
將抽象部分與它的實(shí)現(xiàn)部分分離:實(shí)現(xiàn)系統(tǒng)可能有多角度(維度)分類,每一種分類都可能變化,那么就把這種多角度分離出來讓它們獨(dú)立變化,減少它們之間的耦合。
在發(fā)現(xiàn)需要多角度去分類實(shí)現(xiàn)對象,而只用繼承會造成大量的類增加,不能滿足開放-封閉原則時,就要考慮用Bridge橋接模式了。
合成/聚合復(fù)用原則:盡量使用合成/聚合,精良不要使用類繼承。
優(yōu)先使用對象的合成/聚合將有助于保持每個類被封裝,并被集中在單個任務(wù)上。這樣類和類繼承層次會保持較小規(guī)模,并且不太可能增長為不可控制的龐然大物。
適用場景:
- 你不希望在抽象和它的實(shí)現(xiàn)部分之間有一個固定的綁定關(guān)系。例如這種情況可能是因?yàn)?,在程序運(yùn)行時刻實(shí)現(xiàn)部分應(yīng)可以被選擇或者切換。
- 類的抽象以及它的實(shí)現(xiàn)都應(yīng)該可以通過生成子類的方法加以擴(kuò)充。這時B r i d g e 模式使你可以對不同的抽象接口和實(shí)現(xiàn)部分進(jìn)行組合,并分別對它們進(jìn)行擴(kuò)充。
- 對一個抽象的實(shí)現(xiàn)部分的修改應(yīng)對客戶不產(chǎn)生影響,即客戶的代碼不必重新編譯。
- (C + +)你想對客戶完全隱藏抽象的實(shí)現(xiàn)部分。在C + +中,類的表示在類接口中是可見的。
- 有許多類要生成。這樣一種類層次結(jié)構(gòu)說明你必須將一個對象分解成兩個部分。R u m b a u g h 稱這種類層次結(jié)構(gòu)為“嵌套的普化”(nested generalizations )。
- 你想在多個對象間共享實(shí)現(xiàn)(可能使用引用計(jì)數(shù)),但同時要求客戶并不知道這一點(diǎn)。一個簡單的例子便是C o p l i e n 的S t r i n g 類[ C o p 9 2 ],在這個類中多個對象可以共享同一個字符串表示(S t r i n g R e p )。
上一篇:詳解C++中如何將構(gòu)造函數(shù)或析構(gòu)函數(shù)的訪問權(quán)限定為private
欄 目:C語言
下一篇:舉例剖析C++中引用的本質(zhì)及引用作函數(shù)參數(shù)的使用
本文標(biāo)題:C++設(shè)計(jì)模式編程中使用Bridge橋接模式的完全攻略
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2441.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10c++中inline的用法分析
- 01-10用C++實(shí)現(xiàn)DBSCAN聚類算法
- 01-10全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關(guān)鍵字的使用詳解
- 01-10深入C/C++浮點(diǎn)數(shù)在內(nèi)存中的存儲方式詳解
- 01-10深入理解C/C++混合編程


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達(dá)式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10C#中split用法實(shí)例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置