動機
由于某些類型的固有的實現邏輯,使得它們具有兩個變化的維度,乃至多個維度的變化
那么該如何應對這種多維度的變化?如何利用面向對象技術使得類型可以輕松沿著兩個乃至多個方向變化?而不引入額外的復雜度。
橋模式定義
將抽象部分(業務功能)與實現部分(平臺實現)分離,使得它們都可以獨立地變化。
讓我們直接上代碼,分析一下沒有使用橋模式下的代碼如何難以獨立變化。
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();
}
通過閱讀上述代碼,可以發現一個問題,平臺的變化和版本的是兩個維度的問題,如果平臺增加,那么增加的平臺下又增加多幾個版本,就是不同的平臺下面還分有若干版本,子類數量會增多。類的數量為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.針對實現進行修改。