c++ 設計模式7 (Bridge 橋模式)
4.2 Bridge 橋模式
動機:
由於某些類型的固有的實現邏輯,使得它們具有兩個變化的維度,乃至多個變化的維度。
代碼示例:
實現一個Messager,含有基本功能PlaySound,Connect等,並有PC、Mobile不同的平臺實現 和 精簡、完美等不同業務功能的版本
實現方法1:
Bridge1.cpp
類的個數:1 + n + m*n,數量巨大且不同類之中有大量重復
重構見方法2
1 class Messager{ 2 public: 3 virtual void Login(string username, string password)=0; 4 virtual void SendMessage(string message)=0; 5 virtual void SendPicture(Image image)=0; 6 7 virtual void PlaySound()=0; 8 virtual void DrawShape()=0; 9 virtual void WriteText()=0; 10 virtual void Connect()=0; 11 12 virtual ~Messager(){} 13 }; 14 15 16 //平臺實現 17 18 class PCMessagerBase : public Messager{ 19 public: 20 21 virtual void PlaySound(){ 22 //********** 23 } 24 virtual void DrawShape(){ 25 //********** 26 } 27 virtual void WriteText(){ 28 //********** 29 } 30 virtual void Connect(){ 31 //********** 32 } 33 }; 34 35 class MobileMessagerBase : public Messager{ 36 public: 37 38 virtual void PlaySound(){ 39 //========== 40 } 41 virtual void DrawShape(){ 42 //========== 43 } 44 virtual void WriteText(){ 45 //========== 46 } 47 virtual void Connect(){ 48 //========== 49 } 50 }; 51 52 53 54 //業務抽象 55 56 class PCMessagerLite : public PCMessagerBase { 57 public: 58 59 virtual void Login(string username, string password){ 60 61 PCMessagerBase::Connect(); 62 //........ 63 } 64 virtual void SendMessage(string message){ 65 66 PCMessagerBase::WriteText(); 67 //........ 68 } 69 virtual void SendPicture(Image image){ 70 71 PCMessagerBase::DrawShape(); 72 //........ 73 } 74 }; 75 76 77 78 class PCMessagerPerfect : public PCMessagerBase { 79 public: 80 81 virtual void Login(string username, string password){ 82 83 PCMessagerBase::PlaySound(); 84 //******** 85 PCMessagerBase::Connect(); 86 //........ 87 } 88 virtual void SendMessage(string message){ 89 90 PCMessagerBase::PlaySound(); 91 //******** 92 PCMessagerBase::WriteText(); 93 //........ 94 } 95 virtual void SendPicture(Image image){ 96 97 PCMessagerBase::PlaySound(); 98 //******** 99 PCMessagerBase::DrawShape(); 100 //........ 101 } 102 }; 103 104 105 class MobileMessagerLite : public MobileMessagerBase { 106 public: 107 108 virtual void Login(string username, string password){ 109 110 MobileMessagerBase::Connect(); 111 //........ 112 } 113 virtual void SendMessage(string message){ 114 115 MobileMessagerBase::WriteText(); 116 //........ 117 } 118 virtual void SendPicture(Image image){ 119 120 MobileMessagerBase::DrawShape(); 121 //........ 122 } 123 }; 124 125 126 class MobileMessagerPerfect : public MobileMessagerBase { 127 public: 128 129 virtual void Login(string username, string password){ 130 131 MobileMessagerBase::PlaySound(); 132 //******** 133 MobileMessagerBase::Connect(); 134 //........ 135 } 136 virtual void SendMessage(string message){ 137 138 MobileMessagerBase::PlaySound(); 139 //******** 140 MobileMessagerBase::WriteText(); 141 //........ 142 } 143 virtual void SendPicture(Image image){ 144 145 MobileMessagerBase::PlaySound(); 146 //******** 147 MobileMessagerBase::DrawShape(); 148 //........ 149 } 150 }; 151 152 153 void Process(){ 154 //編譯時裝配 155 Messager *m = 156 new MobileMessagerPerfect(); 157 }
重構步驟:
1.繼承轉組合,將PCMessagerBase,Mobilemessager聲明為字段;
1 class PCMessagerLite { 2 PCMessagerBase *messager; 3 public: 4 5 virtual void Login(string username, string password){ 6 7 messager -> Connect(); 8 //........ 9 } 10 virtual void SendMessage(string message){ 11 12 messager -> WriteText(); 13 //........ 14 } 15 virtual void SendPicture(Image image){ 16 17 messager -> DrawShape(); 18 //........ 19 } 20 }; 21 22 class PCMessagerLite { 23 MobileMessagerBase *messager; 24 public: 25 26 virtual void Login(string username, string password){ 27 28 messager -> Connect(); 29 //........ 30 } 31 virtual void SendMessage(string message){ 32 33 messager -> WriteText(); 34 //........ 35 } 36 virtual void SendPicture(Image image){ 37 38 messager -> DrawShape(); 39 //........ 40 } 41 };
2.觀察上述兩個類,發現只有 *messager 聲明不同,故采用基類聲明,運行時多態調用方式,創建不同的 PCMessagerBase,Mobilemessager;
1 class PCMessagerLite { 2 Messager *messager; // = new PCMessagerBase()或 MobileMessagerBase() 3 public: 4 5 virtual void Login(string username, string password){ 6 7 messager -> Connect(); 8 //........ 9 } 10 virtual void SendMessage(string message){ 11 12 messager -> WriteText(); 13 //........ 14 } 15 virtual void SendPicture(Image image){ 16 17 messager -> DrawShape(); 18 //........ 19 } 20 };
3.考慮步驟2的代碼,Messager類是純虛基類(抽象類),不能實例化,故= new ...不成立。
分析產生這種狀況的原因,是Login,SendPicture等與平臺實現相關的方法,和PlaySound,DrawShape等與業務功能相關的方法不應該在一個類裏。
將其拆分,得到MessagerImp類。
同時將MessagerLite,MessagerPerfect類中相同的MesseagerImp字段提到父類Messager,得到重構後的代碼
註意運行時裝配
1 class Messager{ 2 protected: 3 MessagerImp* messagerImp;//... 4 public: 5 virtual void Login(string username, string password)=0; 6 virtual void SendMessage(string message)=0; 7 virtual void SendPicture(Image image)=0; 8 9 virtual ~Messager(){} 10 }; 11 12 class MessagerImp{ 13 public: 14 virtual void PlaySound()=0; 15 virtual void DrawShape()=0; 16 virtual void WriteText()=0; 17 virtual void Connect()=0; 18 19 virtual MessagerImp(){} 20 }; 21 22 23 //平臺實現 n 24 class PCMessagerImp : public MessagerImp{ 25 public: 26 27 virtual void PlaySound(){ 28 //********** 29 } 30 virtual void DrawShape(){ 31 //********** 32 } 33 virtual void WriteText(){ 34 //********** 35 } 36 virtual void Connect(){ 37 //********** 38 } 39 }; 40 41 class MobileMessagerImp : public MessagerImp{ 42 public: 43 44 virtual void PlaySound(){ 45 //========== 46 } 47 virtual void DrawShape(){ 48 //========== 49 } 50 virtual void WriteText(){ 51 //========== 52 } 53 virtual void Connect(){ 54 //========== 55 } 56 }; 57 58 59 60 //業務抽象 m 61 62 //類的數目:1+n+m 63 64 class MessagerLite :public Messager { 65 66 67 public: 68 69 virtual void Login(string username, string password){ 70 71 messagerImp->Connect(); 72 //........ 73 } 74 virtual void SendMessage(string message){ 75 76 messagerImp->WriteText(); 77 //........ 78 } 79 virtual void SendPicture(Image image){ 80 81 messagerImp->DrawShape(); 82 //........ 83 } 84 }; 85 86 87 88 class MessagerPerfect :public Messager { 89 90 91 public: 92 93 virtual void Login(string username, string password){ 94 95 messagerImp->PlaySound(); 96 //******** 97 messagerImp->Connect(); 98 //........ 99 } 100 virtual void SendMessage(string message){ 101 102 messagerImp->PlaySound(); 103 //******** 104 messagerImp->WriteText(); 105 //........ 106 } 107 virtual void SendPicture(Image image){ 108 109 messagerImp->PlaySound(); 110 //******** 111 messagerImp->DrawShape(); 112 //........ 113 } 114 }; 115 116 117 118 119 void Process(){ 120 //運行時裝配 121 MessagerImp* mImp=new PCMessagerImp(); 122 Messager *m =new Messager(mImp); 123 }
模式定義:
將抽象部分(業務功能)與實現部分(平臺實現)分離,使他們都可以獨立地變化。
類圖:
要點總結:
Bridge模式使用“對象間的組合關系”解耦了抽象和實現之間固有的綁定關系,使得抽象的實現可以沿著各自的維度來變化。所謂抽象和實現研制各自維度的變化,即“子類化”他們。
Bridge模式有時候類似於多繼承方案,但是多繼承方案往往違背單一職責原則(即一個雷只有一個變化的原因),復用性較差。Bridge模式是比多繼承更好的解決方案。
Bridge模式的應用一般在“兩個非常強的變化維度”有時一個類也有多余兩個的變化維度,這是可以使用Bridge的擴展模式。
c++ 設計模式7 (Bridge 橋模式)