【设计模式】 桥梁模式
1、定義
1.1 標準定義
橋梁模式( Bridge Pattern) 也叫做橋接模式, 是一個比較簡單的模式, 其定義如下:
Decouple an abstraction from its implementation so that the two can vary independently.( 將抽象和實現解耦, 使得兩者可以獨立地變化。 )
橋梁模式的重點是在“解耦”上, 如何讓它們兩者解耦是我們要了解的重點。
1.2 通用類圖
● Abstraction——抽象化角色
它的主要職責是定義出該角色的行為, 同時保存一個對實現化角色的引用, 該角色一般是抽象類。
● Implementor——實現化角色
它是接口或者抽象類, 定義角色必需的行為和屬性。
● RefinedAbstraction——修正抽象化角色
它引用實現化角色對抽象化角色進行修正。
● ConcreteImplementor——具體實現化角色
它實現接口或抽象類定義的方法和屬性。
2、實現
2.1 類圖
Abstraction::Operation():定義要實現的操作接口。
AbstractionImplement::Operation():實現抽象類Abstaction所定義操作的接口,由其具體派生類ConcreteImplemenA、ConcreteImplemenA或者其他派生類實現。
在Abstraction::Operation()中根據不同的指針多態調用AbstractionImplement::Operation()函數。
Bridge用于將表示和實現解耦,兩者可以獨立的變化.在Abstraction類中維護一個AbstractionImplement類指針,需要采用不同的實現方式的時候只需要傳入不同的AbstractionImplement派生類就可以了。
橋接模式就將實現與抽象分離開來,使得RefinedAbstraction依賴于抽象的實現,這樣實現了依賴倒轉原則,而不管左邊的抽象如何變化,只要實現方法不變,右邊的具體實現就不需要修改,而右邊的具體實現方法發生變化,只要接口不變,左邊的抽象也不需要修改。
2.2 代碼
2.2.1 抽象操作
// 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);//構造函數virtual void Operation();//實現接口virtual ~RefinedAbstractionA();//析構函數 private:AbstractionImplement* _imp;//私有成員 };class RefinedAbstractionB:public Abstraction { public:RefinedAbstractionB(AbstractionImplement* imp);//構造函數virtual void Operation();//實現接口virtual ~RefinedAbstractionB();//析構函數 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(); }2.2.3 具體操作
// AbstractImplement.h #ifndef _ABSTRACTIONIMPLEMENT_H_ #define _ABSTRACTIONIMPLEMENT_H_//抽象基類,定義了實現的接口 class AbstractionImplement { public:virtual void Operation()=0;//定義操作接口virtual ~AbstractionImplement(); protected:AbstractionImplement(); };// 繼承自AbstractionImplement,是AbstractionImplement的不同實現之一 class ConcreteAbstractionImplementA:public AbstractionImplement { public:ConcreteAbstractionImplementA();void Operation();//實現操作~ConcreteAbstractionImplementA(); protected: };// 繼承自AbstractionImplement,是AbstractionImplement的不同實現之一 class ConcreteAbstractionImplementB:public AbstractionImplement { public:ConcreteAbstractionImplementB();void Operation();//實現操作~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; }2.2.4 調用
// main.cpp #include "Abstraction.h" #include "AbstractionImplement.h" #include <iostream>using namespace std;int main() {/* 將抽象部分與它的實現部分分離,使得它們可以獨立地變化1、抽象Abstraction與實現AbstractionImplement分離;2、抽象部分Abstraction可以變化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp2);3、實現部分AbstractionImplement也可以變化,如new ConcreteAbstractionImplementA()、new ConcreteAbstractionImplementB();*/AbstractionImplement* imp = new ConcreteAbstractionImplementA(); //實現部分ConcreteAbstractionImplementAAbstraction* abs = new RefinedAbstractionA(imp); //抽象部分RefinedAbstractionAabs->Operation();cout << "-----------------------------------------" << endl;AbstractionImplement* imp1 = new ConcreteAbstractionImplementB(); //實現部分ConcreteAbstractionImplementBAbstraction* abs1 = new RefinedAbstractionA(imp1); //抽象部分RefinedAbstractionAabs1->Operation();cout << "-----------------------------------------" << endl;AbstractionImplement* imp2 = new ConcreteAbstractionImplementA(); //實現部分ConcreteAbstractionImplementAAbstraction* abs2 = new RefinedAbstractionB(imp2); //抽象部分RefinedAbstractionBabs2->Operation();cout << "-----------------------------------------" << endl;AbstractionImplement* imp3 = new ConcreteAbstractionImplementB(); //實現部分ConcreteAbstractionImplementBAbstraction* abs3 = new RefinedAbstractionB(imp3); //抽象部分RefinedAbstractionBabs3->Operation();cout << endl;return 0; }2.2.5 代碼說明
Bridge模式將抽象和實現分別獨立實現,在代碼中就是Abstraction類和AbstractionImplement類。
使用組合(委托)的方式將抽象和實現徹底地解耦,這樣的好處是抽象和實現可以分別獨立地變化,系統的耦合性也得到了很好的降低。
GoF的那句話中的“實現”該怎么去理解:“實現”特別是和“抽象”放在一起的時候我們“默認”的理解是“實現”就是“抽象”的具體子類的實現,但是這里GoF所謂的“實現”的含義不是指抽象基類的具體子類對抽象基類中虛函數(接口)的實現,是和繼承結合在一起的。而這里的“實現”的含義指的是怎么去實現用戶的需求,并且指的是通過組合(委托)的方式實現的,因此這里的實現不是指的繼承基類、實現基類接口,而是指的是通過對象組合實現用戶的需求。
實際上上面使用Bridge模式和使用帶來問題方式的解決方案的根本區別在于是通過繼承還是通過組合的方式去實現一個功能需求。
3、總結
3.1 優點
● 抽象和實現分離
這也是橋梁模式的主要特點, 它完全是為了解決繼承的缺點而提出的設計模式。 在該模式下, 實現可以不受抽象的約束, 不用再綁定在一個固定的抽象層次上,將實現抽離出來,再實現抽象,使得對象的具體實現依賴于抽象,滿足了依賴倒轉原則。
● 優秀的擴充能力
看看我們的例子, 想增加實現? 沒問題! 想增加抽象, 也沒有問題! 只要對外暴露的接口層允許這樣的變化, 我們已經把變化的可能性減到最小。
● 實現細節對客戶透明
客戶不用關心細節的實現, 它已經由抽象層通過聚合關系完成了封裝。
● 減少代碼重復
將可以共享的變化部分,抽離出來,減少了代碼的重復信息。
● 更加靈活
對象的具體實現可以更加靈活,可以滿足多個因素變化的要求。
3.2 缺點
客戶必須知道選擇哪一種類型的實現。
3.3 使用場景
● 不希望或不適用使用繼承的場景
例如繼承層次過渡、 無法更細化設計顆粒等場景, 需要多角度去分類實現對象,而只用繼承會造成大量的類增加,不能滿足開放-封閉原則時,就要考慮用Bridge橋接模式了。
● 接口或抽象類不穩定的場景
明知道接口不穩定還想通過實現或繼承來實現業務需求, 那是得不償失的, 也是比較失敗的做法。
● 重用性要求較高的場景
設計的顆粒度越細, 則被重用的可能性就越大, 而采用繼承則受父類的限制, 不可能出現太細的顆粒度。當多個變化因素在多個對象間共享時,考慮將這部分變化的部分抽象出來再聚合/合成進來。
● 變化較多的場景
當一個對象有多個變化因素的時候,考慮依賴于抽象的實現,而不是具體的實現。
3.4 注意事項
橋梁模式是非常簡單的, 使用該模式時主要考慮如何拆分抽象和實現, 并不是一涉及繼承就要考慮使用該模式, 那還要繼承干什么呢? 橋梁模式的意圖還是對變化的封裝, 盡量把可能變化的因素封裝到最細、 最小的邏輯單元中, 避免風險擴散。 因此讀者在進行系統設計時, 發現類的繼承有N層時, 可以考慮使用橋梁模式。
由于實現的方式有多種,橋接模式的核心就是把這些實現獨立出來,讓他們各自變化。
將抽象部分與它的實現部分分離:實現系統可能有多角度(維度)分類,每一種分類都可能變化,那么就把這種多角度分離出來讓它們獨立變化,減少它們之間的耦合。
合成/聚合復用原則:盡量使用合成/聚合,盡量不要使用類繼承。
優先使用對象的合成/聚合將有助于保持每個類被封裝,并被集中在單個任務上。這樣類和類繼承層次會保持較小規模,并且不太可能增長為不可控制的龐然大物。
3.5 橋梁模式VS建造者模式
Bridge的實現方式其實和Builde十分的相近,可以這么說:本質上是一樣的,只是封裝的東西不一樣罷了。兩者的實現都有如下的共同點:
抽象出來一個基類,這個基類里面定義了共有的一些行為,形成接口函數(對接口編程而不是對實現編程),這個接口函數在Buildier中是BuildePart函數在Bridge中是Operation函數;
其次,聚合一個基類的指針,如Builder模式中Director類聚合了一個Builder基類的指針,而Brige模式中Abstraction類聚合了一個AbstractionImplement基類的指針(優先采用聚合而不是繼承);
而在使用的時候,都把對這個類的使用封裝在一個函數中,在Bridge中是封裝在Director::Construct函數中,因為裝配不同部分的過程是一致的,而在Bridge模式中則是封裝在Abstraction::Operation函數中,在這個函數中調用對應的AbstractionImplement::Operation函數.就兩個模式而言,Builder封裝了不同的生成組成部分的方式,而Bridge封裝了不同的實現方式。
轉載于:https://www.cnblogs.com/ChinaHook/p/7309773.html
總結
以上是生活随笔為你收集整理的【设计模式】 桥梁模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 正则表达式: 正向预查和负向预查
- 下一篇: ES6常用方法总结