【GeekBand】C++面向對象高級編程-第九周筆記

動機

由于某些類型的固有的實現邏輯,使得它們具有兩個變化的維度,乃至多個維度的變化
那么該如何應對這種多維度的變化?如何利用面向對象技術使得類型可以輕松沿著兩個乃至多個方向變化?而不引入額外的復雜度。

橋模式定義

將抽象部分(業務功能)與實現部分(平臺實現)分離,使得它們都可以獨立地變化。
讓我們直接上代碼,分析一下沒有使用橋模式下的代碼如何難以獨立變化。

class Messager{
public:
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;

    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;
    
    virtual ~Messager(){}
};


//平臺實現

class PCMessagerBase : public Messager{
public:
    
    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};

class MobileMessagerBase : public Messager{
public:
    
    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};



//業務抽象

class PCMessagerLite : public PCMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        PCMessagerBase::DrawShape();
        //........
    }
};



class PCMessagerPerfect : public PCMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::DrawShape();
        //........
    }
};


class MobileMessagerLite : public MobileMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        MobileMessagerBase::DrawShape();
        //........
    }
};


class MobileMessagerPerfect : public MobileMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::DrawShape();
        //........
    }
};


void Process(){
        //編譯時裝配
        Messager *m =
            new MobileMessagerPerfect();
}
類圖.JPG

通過閱讀上述代碼,可以發現一個問題,平臺的變化和版本的是兩個維度的問題,如果平臺增加,那么增加的平臺下又增加多幾個版本,就是不同的平臺下面還分有若干版本,子類數量會增多。類的數量為1+n+nm*。
課程中提到橋模式,讓我們一起來看看橋模式。
分析上述代碼,
PCMessagerLite 和 MobilMessagerLite內的函數基本相同,不同的是平臺不同,Prefect版本也類似。那么可以嘗試將其進行合并和簡化。

class Messager{
public:
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;

    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;
    
    virtual ~Messager(){}
};

這個類中的存在兩個變化,需要將其進行拆分。

class Messager{
public:
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;
    virtual ~Messager(){}
};
class MessagerImg{
public:
    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;
    virtual ~MessagerImg(){}
};

這樣子,就可以分別修改這兩個維度的代碼了。
拆分后,平臺只和MessagerImg有關,從而修改平臺相關的代碼


class PCMessagerImg : public MessagerImg{
public:
    
    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};
class MobileMessagerImg : public MessagerImg{
public:
    
    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};

由于平臺類有一個接口,那么利用多態性,則可以對平臺相關的業務抽象進行簡化。

class PCMessagerLite {
   MessagerImg *mImg;
public:
    
    virtual void Login(string username, string password){
        
       mImg->Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        mImg->PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        mImg->DrawShape();
        //........
    }
};

經過這么修改,可以發現版本和平臺不是繼承的關系,如果Mobile的代碼也如此修改,那么可以發現其內部成員函數是一樣的,則可以刪除其中一個,Lite版本可以合并為:

class MessagerLite:public Messager {
   MessagerImg *mImg;
public:
    virtual void Login(string username, string password){
        
       mImg->Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        mImg->PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        mImg->DrawShape();
        //........
    }
};

類似地Perfect版本可以改為:

class MessagerPerfect:public Messager {
   MessagerImg *mImg;
public:
    virtual void Login(string username, string password){
        
       mImg->Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        mImg->PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        mImg->DrawShape();
        //........
    }
};

根據重構一書,如果兩個子類有同樣的成員變量,可以將其提升到基類中,并且添加對象的構造函數來接受不同平臺的對象,則Messager類改為

class Messager{
protected:
       MessagerImg *mImg;
public:
    Messager(MessagerImg *_mImg):mImg(_mImg) {}
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;
    virtual ~Messager(){}
};

同樣,其子類也要有對象的構造函數來初始化父類中的mImg。
最后代碼改為:

class Messager{
protected:
    MessagerImp *mImp;
public:
    Messager(MessagerImp *_mImp) :mImp(_mImp) { }
    virtual void Login(string username, string password) = 0;
    virtual void SendMessage(string _message) = 0;
    virtual void SendPicture(Image image) = 0;
    virtual ~Messager(){}
};
class MessagerImp{
public:
    virtual void PlaySound() = 0;
    virtual void DrawShape() = 0;
    virtual void WriteText() = 0;
    virtual void Connect() = 0;
    virtual ~MessagerImp() {}
};

//平臺實現

class PCMessagerImp : public MessagerImp{
public:

    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};
class MobileMessagerImp : public MessagerImp{
public:

    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};

//業務抽象

class MessagerLite :public Messager {
public:
    MessagerPerfect(MessagerImp *_messagerImp) :Messager(_messagerImp) { }
    virtual void Login(string username, string password){

        messageImp->Connect();
        //........
    }
    virtual void SendMessage(string _message){

        messageImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        messageImp->DrawShape();
        //........
    }
};



class MessagerPerfect :public Messager{
public:
    MessagerPerfect(MessagerImp *_messagerImp) :Messager(_messagerImp){ }
    virtual void Login(string username, string password){

        messagerImp->PlaySound();
        //********
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string _message){

        messagerImp->PlaySound();
        //********
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){

        messagerImp->PlaySound();
        //********
        messagerImp->DrawShape();
        //........
    }
};

void Process(){
    //運行時裝配
    MessagerImp *messagerImp = new PCMessagerImg();
    Messager *m = new MessagerPerfect(messagerImp);
}

所以總結一下:
橋模式使用對象間的組合關系解耦了抽象和實現之間固有的綁定關系,使得抽象和實現可以沿著各自的維度來變化。所謂抽象和實現沿著各自維度的變化,即子類化它們。
一樣應用在兩個非常強的變化維度,有時一個類也有多于兩個的變化維度,這時可以使用Bridge的擴展模式(加指針)
具體步驟是:
1.分離該類中的不同維度的成員;
2.抽象維度的實現維度指針指向實現維度,從而產生聯系。這個指針便是“橋”。
3.針對實現進行修改。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容