將抽象部份與它的實現部份分離,使它們都可以獨立地變化。
假設現在需要大、中、小、三種型號的畫筆來繪制7種不同的顏色,如果使用蠟筆,就需要准備 3*7 = 21支蠟筆。而如果使用毛筆,則只需要3支毛筆和7中顏料。顯然使用毛筆的形式要比蠟筆簡單許多,這其中緣由在於在蠟筆這個對象中型號和顏料兩個不同維度的因素耦合太強,而毛筆的型號和顏色屬於松耦合的關系。 在毛筆實例中,型號大小和顏色是分離的,可以獨立變化的。 這就是橋接模式的基本思想:將不同維度的變化職責抽取出來形成各自的繼承等級結構,從而能夠讓其分別獨立變化。
我們給出橋接模式的UML類圖如下:
可以看到,在橋接模式中,存著左右兩棵繼承樹,Abstraction
和Implementor
,兩棵繼承樹可以獨立發展變化,互不干擾。
有這樣的一個情景,在不同的操作系統上,音頻解碼的算法都是一致的,而播放聲音的方法卻不一致。我們要實現一個播放器能夠在不同的操作系統上播放不同格式的音樂,這裡有兩個變化的維度:音頻格式和操作系統。在學習橋接模式之前,我們的解決方案如下:
顯然因為我們將音頻解碼和播放音頻的職責設計成了強耦合,於是我們的系統結構變得復雜了,這就是違反單一職責的後果。於是我們使用了橋接模式,將操作系統播放音頻的職責抽取出來,成為一棵新的繼承樹,讓播放音頻和音頻解碼兩個職責獨立變化。這樣播放器的音頻播放部分被抽離了,也就是抽象和實現分離。使用了橋接模式的UML類圖如下:
可以看到,使用了橋接模式之後的解決方案十分清晰明了。橋接模式的好處由於可見一斑。
下面給出橋接模式的示例代碼
#include
#include
using std::string;
class OperatinSystem {
public:
OperatinSystem (){};
virtual ~OperatinSystem (){};
virtual void doPlay() = 0;
};
class Linux : public OperatinSystem {
public:
Linux (){};
virtual ~Linux(){};
virtual void doPlay(){
std::cout << " Linux" << std::endl;
}
};
class Unix : public OperatinSystem {
public:
Unix(){};
virtual ~Unix(){};
virtual void doPlay(){
std::cout << " Unix" << std::endl;
}
};
class Windows : public OperatinSystem {
public:
Windows (){};
virtual ~Windows(){};
virtual void doPlay(){
std::cout << " Windows" << std::endl;
}
};
class Player {
protected:
OperatinSystem *OS;
public:
Player (): OS(NULL){};
virtual void setOS(OperatinSystem *os){OS = os;}
virtual ~Player (){};
virtual void play() = 0;
};
class MP3Player : public Player{
public:
MP3Player(){};
virtual ~MP3Player(){};
virtual void play(){
std::cout << "Play MP3 under ";
OS->doPlay();
}
};
class WAVPlayer : public Player{
public:
WAVPlayer(){};
virtual ~WAVPlayer(){};
virtual void play(){
std::cout << "Play WAV under ";
OS->doPlay();
}
};
class WMAPlayer : public Player{
public:
WMAPlayer(){};
virtual ~WMAPlayer(){};
virtual void play(){
std::cout << "Play WMA under ";
OS->doPlay();
}
};
int main(void)
{
Player *mp3Player = new MP3Player;
mp3Player->setOS(new Linux);
mp3Player->play();
mp3Player->setOS(new Windows);
mp3Player->play();
mp3Player->setOS(new Unix);
mp3Player->play();
Player *wmalayer = new WMAPlayer;
wmalayer->setOS(new Linux);
wmalayer->play();
wmalayer->setOS(new Windows);
wmalayer->play();
wmalayer->setOS(new Unix);
wmalayer->play();
Player *wavPlayer = new WAVPlayer;
wavPlayer->setOS(new Linux);
wavPlayer->play();
wavPlayer->setOS(new Windows);
wavPlayer->play();
wavPlayer->setOS(new Unix);
wavPlayer->play();
}
運行結果:
Play MP3 under Linux
Play MP3 under Windows
Play MP3 under Unix
Play WMA under Linux
Play WMA under Windows
Play WMA under Unix
Play WAV under Linux
Play WAV under Windows
Play WAV under Unix
橋接模式的優點 :
分離了抽象和實現。橋接模式使用了聚合代替了原本類間的綁定關系(繼承具有侵入性),實現松耦合。
變化的因素之間相互隔離,獨立變化,方便拓展。橋接模式在增加系統的新實現的時候,不需要修改已有類,符合開閉原則。
橋接模式的適用場景:
當繼承樹過於復雜時,應該考慮使用橋接模式。當一個類中存在著多個變化的維度的時候,應該考慮橋接模式。