動機
由于某些類型的固有的實現(xiàn)邏輯,使得它們具有兩個變化的維度腕让,乃至多個維度的變化
那么該如何應(yīng)對這種多維度的變化孤钦?如何利用面向?qū)ο蠹夹g(shù)使得類型可以輕松沿著兩個乃至多個方向變化?而不引入額外的復(fù)雜度纯丸。
橋模式定義
將抽象部分(業(yè)務(wù)功能)與實現(xiàn)部分(平臺實現(xiàn))分離偏形,使得它們都可以獨立地變化。
讓我們直接上代碼觉鼻,分析一下沒有使用橋模式下的代碼如何難以獨立變化俊扭。
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(){}
};
//平臺實現(xiàn)
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(){
//==========
}
};
//業(yè)務(wù)抽象
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();
}
通過閱讀上述代碼,可以發(fā)現(xiàn)一個問題坠陈,平臺的變化和版本的是兩個維度的問題萨惑,如果平臺增加捐康,那么增加的平臺下又增加多幾個版本,就是不同的平臺下面還分有若干版本庸蔼,子類數(shù)量會增多吹由。類的數(shù)量為1+n+nm*。
課程中提到橋模式朱嘴,讓我們一起來看看橋模式。
分析上述代碼粗合,
PCMessagerLite 和 MobilMessagerLite內(nèi)的函數(shù)基本相同萍嬉,不同的是平臺不同,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有關(guān)悼做,從而修改平臺相關(guān)的代碼
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(){
//==========
}
};
由于平臺類有一個接口,那么利用多態(tài)性哗魂,則可以對平臺相關(guān)的業(yè)務(wù)抽象進行簡化肛走。
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();
//........
}
};
經(jīng)過這么修改,可以發(fā)現(xiàn)版本和平臺不是繼承的關(guān)系录别,如果Mobile的代碼也如此修改朽色,那么可以發(fā)現(xiàn)其內(nèi)部成員函數(shù)是一樣的,則可以刪除其中一個组题,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();
//........
}
};
根據(jù)重構(gòu)一書葫男,如果兩個子類有同樣的成員變量,可以將其提升到基類中崔列,并且添加對象的構(gòu)造函數(shù)來接受不同平臺的對象梢褐,則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(){}
};
同樣,其子類也要有對象的構(gòu)造函數(shù)來初始化父類中的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() {}
};
//平臺實現(xiàn)
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(){
//**********
}
};
//業(yè)務(wù)抽象
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);
}
所以總結(jié)一下:
橋模式使用對象間的組合關(guān)系解耦了抽象和實現(xiàn)之間固有的綁定關(guān)系利职,使得抽象和實現(xiàn)可以沿著各自的維度來變化。所謂抽象和實現(xiàn)沿著各自維度的變化瘦癌,即子類化它們猪贪。
一樣應(yīng)用在兩個非常強的變化維度,有時一個類也有多于兩個的變化維度讯私,這時可以使用Bridge的擴展模式(加指針)
具體步驟是:
1.分離該類中的不同維度的成員热押;
2.抽象維度的實現(xiàn)維度指針指向?qū)崿F(xiàn)維度西傀,從而產(chǎn)生聯(lián)系。這個指針便是“橋”桶癣。
3.針對實現(xiàn)進行修改拥褂。