C++设计模式-Bridge桥接模式
作用:將抽象部份與它的實(shí)現(xiàn)部份分離,使它們都可以獨(dú)立地變化。
將抽象(Abstraction)與實(shí)現(xiàn)(Implementation)分離,使得二者可以獨(dú)立地變化。
橋接模式號(hào)稱設(shè)計(jì)模式中最難理解的模式之一,關(guān)鍵就是這個(gè)抽象和實(shí)現(xiàn)的分離非常讓人奇怪,大部分人剛看到這個(gè)定義的時(shí)候都會(huì)認(rèn)為實(shí)現(xiàn)就是繼承自抽象,那怎么可能將他們分離呢。
《大話設(shè)計(jì)模式》中就Bridge模式的解釋:
手機(jī)品牌和軟件是兩個(gè)概念,不同的軟件可以在不同的手機(jī)上,不同的手機(jī)可以有相同的軟件,兩者都具有很大的變動(dòng)性。如果我們單獨(dú)以手機(jī)品牌或手機(jī)軟件為基類來進(jìn)行繼承擴(kuò)展的話,無疑會(huì)使類的數(shù)目劇增并且耦合性很高,(如果更改品牌或增加軟件都會(huì)增加很多的變動(dòng))兩種方式的結(jié)構(gòu)如下:
?
所以將兩者抽象出來兩個(gè)基類分別是PhoneBrand和PhoneSoft,那么在品牌類中聚合一個(gè)軟件對(duì)象的基類將解決軟件和手機(jī)擴(kuò)展混亂的問題,這樣兩者的擴(kuò)展就相對(duì)靈活,剪短了兩者的必要聯(lián)系,結(jié)構(gòu)圖如下:
這樣擴(kuò)展品牌和軟件就相對(duì)靈活獨(dú)立,達(dá)到解耦的目的!
?UML結(jié)構(gòu)圖如下:
抽象基類及接口:
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ù)一個(gè)AbstractionImplement類指針,需要采用不同的實(shí)現(xiàn)方式的時(shí)候只需要傳入不同的AbstractionImplement派生類就可以了.
Bridge的實(shí)現(xiàn)方式其實(shí)和Builde十分的相近,可以這么說:本質(zhì)上是一樣的,只是封裝的東西不一樣罷了.兩者的實(shí)現(xiàn)都有如下的共同點(diǎn):
抽象出來一個(gè)基類,這個(gè)基類里面定義了共有的一些行為,形成接口函數(shù)(對(duì)接口編程而不是對(duì)實(shí)現(xiàn)編程),這個(gè)接口函數(shù)在Buildier中是BuildePart函數(shù)在Bridge中是Operation函數(shù);
其次,聚合一個(gè)基類的指針,如Builder模式中Director類聚合了一個(gè)Builder基類的指針,而Brige模式中Abstraction類聚合了一個(gè)AbstractionImplement基類的指針(優(yōu)先采用聚合而不是繼承);
而在使用的時(shí)候,都把對(duì)這個(gè)類的使用封裝在一個(gè)函數(shù)中,在Bridge中是封裝在Director::Construct函數(shù)中,因?yàn)檠b配不同部分的過程是一致的,而在Bridge模式中則是封裝在Abstraction::Operation函數(shù)中,在這個(gè)函數(shù)中調(diào)用對(duì)應(yīng)的AbstractionImplement::Operation函數(shù).就兩個(gè)模式而言,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ā)生變化,只要接口不變,左邊的抽象也不需要修改。
常用的場(chǎng)景
1.當(dāng)一個(gè)對(duì)象有多個(gè)變化因素的時(shí)候,考慮依賴于抽象的實(shí)現(xiàn),而不是具體的實(shí)現(xiàn)。如上面例子中手機(jī)品牌有2種變化因素,一個(gè)是品牌,一個(gè)是功能。
2.當(dāng)多個(gè)變化因素在多個(gè)對(duì)象間共享時(shí),考慮將這部分變化的部分抽象出來再聚合/合成進(jìn)來,如上面例子中的通訊錄和游戲,其實(shí)是可以共享的。
3.當(dāng)我們考慮一個(gè)對(duì)象的多個(gè)變化因素可以動(dòng)態(tài)變化的時(shí)候,考慮使用橋接模式,如上面例子中的手機(jī)品牌是變化的,手機(jī)的功能也是變化的,所以將他們分離出來,獨(dú)立的變化。
優(yōu)點(diǎn)
1.將實(shí)現(xiàn)抽離出來,再實(shí)現(xiàn)抽象,使得對(duì)象的具體實(shí)現(xiàn)依賴于抽象,滿足了依賴倒轉(zhuǎn)原則。
2.將可以共享的變化部分,抽離出來,減少了代碼的重復(fù)信息。
3.對(duì)象的具體實(shí)現(xiàn)可以更加靈活,可以滿足多個(gè)因素變化的要求。
缺點(diǎn)
1.客戶必須知道選擇哪一種類型的實(shí)現(xiàn)。
?
設(shè)計(jì)中有超過一維的變化我們就可以用橋模式。如果只有一維在變化,那么我們用繼承就可以圓滿的解決問題。
?代碼如下:
Abstraction.h
1 #ifndef _ABSTRACTION_H_ 2 #define _ABSTRACTION_H_ 3 4 class AbstractionImplement; 5 6 class Abstraction 7 { 8 public: 9 virtual void Operation()=0;//定義接口,表示該類所支持的操作 10 virtual ~Abstraction(); 11 protected: 12 Abstraction(); 13 }; 14 15 class RefinedAbstractionA:public Abstraction 16 { 17 public: 18 RefinedAbstractionA(AbstractionImplement* imp);//構(gòu)造函數(shù) 19 virtual void Operation();//實(shí)現(xiàn)接口 20 virtual ~RefinedAbstractionA();//析構(gòu)函數(shù) 21 private: 22 AbstractionImplement* _imp;//私有成員 23 }; 24 25 class RefinedAbstractionB:public Abstraction 26 { 27 public: 28 RefinedAbstractionB(AbstractionImplement* imp);//構(gòu)造函數(shù) 29 virtual void Operation();//實(shí)現(xiàn)接口 30 virtual ~RefinedAbstractionB();//析構(gòu)函數(shù) 31 private: 32 AbstractionImplement* _imp;//私有成員 33 }; 34 #endifAbstraction.cpp
1 #include "Abstraction.h" 2 #include "AbstractionImplement.h" 3 #include <iostream> 4 5 using namespace std; 6 7 Abstraction::Abstraction() 8 {} 9 10 Abstraction::~Abstraction() 11 {} 12 13 RefinedAbstractionA::RefinedAbstractionA(AbstractionImplement* imp) 14 { 15 this->_imp = imp; 16 } 17 18 RefinedAbstractionA::~RefinedAbstractionA() 19 { 20 delete this->_imp; 21 this->_imp = NULL; 22 } 23 24 void RefinedAbstractionA::Operation() 25 { 26 cout << "RefinedAbstractionA::Operation" << endl; 27 this->_imp->Operation(); 28 } 29 30 RefinedAbstractionB::RefinedAbstractionB(AbstractionImplement* imp) 31 { 32 this->_imp = imp; 33 } 34 35 RefinedAbstractionB::~RefinedAbstractionB() 36 { 37 delete this->_imp; 38 this->_imp = NULL; 39 } 40 41 void RefinedAbstractionB::Operation() 42 { 43 cout << "RefinedAbstractionB::Operation" << endl; 44 this->_imp->Operation(); 45 }AbstractImplement.h
1 #ifndef _ABSTRACTIONIMPLEMENT_H_ 2 #define _ABSTRACTIONIMPLEMENT_H_ 3 4 //抽象基類,定義了實(shí)現(xiàn)的接口 5 class AbstractionImplement 6 { 7 public: 8 virtual void Operation()=0;//定義操作接口 9 virtual ~AbstractionImplement(); 10 protected: 11 AbstractionImplement(); 12 }; 13 14 // 繼承自AbstractionImplement,是AbstractionImplement的不同實(shí)現(xiàn)之一 15 class ConcreteAbstractionImplementA:public AbstractionImplement 16 { 17 public: 18 ConcreteAbstractionImplementA(); 19 void Operation();//實(shí)現(xiàn)操作 20 ~ConcreteAbstractionImplementA(); 21 protected: 22 }; 23 24 // 繼承自AbstractionImplement,是AbstractionImplement的不同實(shí)現(xiàn)之一 25 class ConcreteAbstractionImplementB:public AbstractionImplement 26 { 27 public: 28 ConcreteAbstractionImplementB(); 29 void Operation();//實(shí)現(xiàn)操作 30 ~ConcreteAbstractionImplementB(); 31 protected: 32 }; 33 #endifAbstractImplement.cpp
1 #include "AbstractionImplement.h" 2 #include <iostream> 3 4 using namespace std; 5 6 AbstractionImplement::AbstractionImplement() 7 {} 8 9 AbstractionImplement::~AbstractionImplement() 10 {} 11 12 ConcreteAbstractionImplementA::ConcreteAbstractionImplementA() 13 {} 14 15 ConcreteAbstractionImplementA::~ConcreteAbstractionImplementA() 16 {} 17 18 void ConcreteAbstractionImplementA::Operation() 19 { 20 cout << "ConcreteAbstractionImplementA Operation" << endl; 21 } 22 23 ConcreteAbstractionImplementB::ConcreteAbstractionImplementB() 24 {} 25 26 ConcreteAbstractionImplementB::~ConcreteAbstractionImplementB() 27 {} 28 29 void ConcreteAbstractionImplementB::Operation() 30 { 31 cout << "ConcreteAbstractionImplementB Operation" << endl; 32 }main.cpp
1 #include "Abstraction.h" 2 #include "AbstractionImplement.h" 3 #include <iostream> 4 5 using namespace std; 6 7 int main() 8 { 9 /* 將抽象部分與它的實(shí)現(xiàn)部分分離,使得它們可以獨(dú)立地變化 10 11 1、抽象Abstraction與實(shí)現(xiàn)AbstractionImplement分離; 12 13 2、抽象部分Abstraction可以變化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp2); 14 15 3、實(shí)現(xiàn)部分AbstractionImplement也可以變化,如new ConcreteAbstractionImplementA()、new ConcreteAbstractionImplementB(); 16 17 */ 18 19 AbstractionImplement* imp = new ConcreteAbstractionImplementA(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementA 20 Abstraction* abs = new RefinedAbstractionA(imp); //抽象部分RefinedAbstractionA 21 abs->Operation(); 22 23 cout << "-----------------------------------------" << endl; 24 25 AbstractionImplement* imp1 = new ConcreteAbstractionImplementB(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementB 26 Abstraction* abs1 = new RefinedAbstractionA(imp1); //抽象部分RefinedAbstractionA 27 abs1->Operation(); 28 29 cout << "-----------------------------------------" << endl; 30 31 AbstractionImplement* imp2 = new ConcreteAbstractionImplementA(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementA 32 Abstraction* abs2 = new RefinedAbstractionB(imp2); //抽象部分RefinedAbstractionB 33 abs2->Operation(); 34 35 cout << "-----------------------------------------" << endl; 36 37 AbstractionImplement* imp3 = new ConcreteAbstractionImplementB(); //實(shí)現(xiàn)部分ConcreteAbstractionImplementB 38 Abstraction* abs3 = new RefinedAbstractionB(imp3); //抽象部分RefinedAbstractionB 39 abs3->Operation(); 40 41 cout << endl; 42 return 0; 43 } 代碼說明:
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)”特別是和“抽象”放在一起的時(shí)候我們“默認(rèn)”的理解是“實(shí)現(xiàn)”就是“抽象”的具體子類的實(shí)現(xiàn),但是這里GoF所謂的“實(shí)現(xiàn)”的含義不是指抽象基類的具體子類對(duì)抽象基類中虛函數(shù)(接口)的實(shí)現(xiàn),是和繼承結(jié)合在一起的。而這里的“實(shí)現(xiàn)”的含義指的是怎么去實(shí)現(xiàn)用戶的需求,并且指的是通過組合(委托)的方式實(shí)現(xiàn)的,因此這里的實(shí)現(xiàn)不是指的繼承基類、實(shí)現(xiàn)基類接口,而是指的是通過對(duì)象組合實(shí)現(xiàn)用戶的需求。
實(shí)際上上面使用Bridge模式和使用帶來問題方式的解決方案的根本區(qū)別在于是通過繼承還是通過組合的方式去實(shí)現(xiàn)一個(gè)功能需求。
備注:
由于實(shí)現(xiàn)的方式有多種,橋接模式的核心就是把這些實(shí)現(xiàn)獨(dú)立出來,讓他們各自變化。
將抽象部分與它的實(shí)現(xiàn)部分分離:實(shí)現(xiàn)系統(tǒng)可能有多角度(維度)分類,每一種分類都可能變化,那么就把這種多角度分離出來讓它們獨(dú)立變化,減少它們之間的耦合。
在發(fā)現(xiàn)需要多角度去分類實(shí)現(xiàn)對(duì)象,而只用繼承會(huì)造成大量的類增加,不能滿足開放-封閉原則時(shí),就要考慮用Bridge橋接模式了。
合成/聚合復(fù)用原則:盡量使用合成/聚合,精良不要使用類繼承。
優(yōu)先使用對(duì)象的合成/聚合將有助于保持每個(gè)類被封裝,并被集中在單個(gè)任務(wù)上。這樣類和類繼承層次會(huì)保持較小規(guī)模,并且不太可能增長為不可控制的龐然大物。
總結(jié)
以上是生活随笔為你收集整理的C++设计模式-Bridge桥接模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAXP
- 下一篇: Clumsy网络模拟工具