S5. 桥接模式
桥接模式
桥接是一种结构型设计模式,可将业务逻辑或一个大类拆分为不同的层次结构, 从而能独立地进行开发。
简单的理解,就是将抽象部分与实现部分分离,实现解耦。
场景
实现Honor30Pro与Mate40手机的微信与王者荣耀APP。
分析
遇到此类场景时,通常我们会抽象出手机类,然后在不同的手机上实现客制化的APP(假设不同的手机品牌安装同一款APP会有一些参数差异)。
如果将手机抽象,可以得到如下类图:

如上设计是将各个品牌手机相同部分抽象出来,差异性的部分可在具体的子类中实现。因此也就能够实现具备王者荣耀和微信的不同品牌手机(Honor、Mate40)。
但是上述设计又暴露出如下问题:
-
每增加一款手机,就要重新实现一个手机子类以及适应当前手机的各个APP。
-
每个手机与其平台上的APP高度耦合。例如,当Hono30Pro类发生修改时,其子类王者荣耀APP以及微信APP都需要随之修改。
-
这种设计还会导致APP与手机绑定,从而无法实现从某个手机卸载指定的APP。例:在Mate40手机卸载王者荣耀。
将App抽象
将APP抽象时,得到可用于Mate40与Honor30 Pro的微信与王者荣耀。

此种设计与第一种设计存在相同的问题。
将手机与APP分别抽象
当回头再分析场景时,发现场景存在两个实例,手机和APP,且为“has a”的关系,即聚合。那么可将手机与APP分别抽象,实现聚合的关系,子类无需关心两者间的关系。

如上设计,仅CPhone类与CAPP类有耦合关系。在实际的实现中,CPhone与CAPP为依赖关系,具体CPhone子类与CAPP为依赖和聚合关系。将原先高度耦合的关系,转换为CPhone子类与CAPP抽象类耦合。
如此设计有以下优点:
- 完成CPhone子类与CApp子类的解耦。两者都可以独立修改,互不干扰。灵活性更强。
- 更容易扩展。当需要增加新的手机或APP时,能够在不修改原有的代码基础上完成。
代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class CApp
{
public:
CApp()
{
}
virtual ~CApp()
{
}
virtual string GetName() = 0;
virtual void ShowLogo() = 0;
virtual void RunPlatform(string platform) = 0;
};
class CHonorOfKings : public CApp
{
public:
CHonorOfKings(string logo)
{
mLogo = logo;
mName = "HonorOfKings";
}
~CHonorOfKings()
{
}
string GetName()
{
return mName;
}
void ShowLogo()
{
cout << mLogo << " (" << mPlatform << ")" << endl;
}
void RunPlatform(string platform)
{
mPlatform = platform;
}
private:
string mName;
string mLogo;
string mPlatform;
};
class CWeChat : public CApp
{
public:
CWeChat(string logo)
{
mLogo = logo;
mName = "WeChat";
}
~CWeChat()
{
}
string GetName()
{
return mName;
}
void ShowLogo()
{
cout << mLogo << " (" << mPlatform << ")" << endl;
}
void RunPlatform(string platform)
{
mPlatform = platform;
}
private:
string mName;
string mLogo;
string mPlatform;
};
class CPhone
{
public:
virtual ~CPhone()
{
}
virtual void InstallApp(CApp *pApp) = 0;
virtual void Uninstall(CApp *pApp) = 0;
virtual void EnterApp(CApp *pApp) = 0;
virtual void ShowAppList() = 0;
};
class CMate40: public CPhone
{
public:
explicit CMate40(string name)
{
mName = name;
}
~CMate40()
{
}
void InstallApp(CApp *pApp)
{
pApp->RunPlatform(mName);
mAppVec.push_back(pApp);
}
void Uninstall(CApp *pApp)
{
vector<CApp *>::iterator it;
for (it = mAppVec.begin(); it != mAppVec.end(); ) {
if ((*it)->GetName() == pApp->GetName()) {
cout << "Uninstall " << (*it)->GetName() << " Success!" << endl;
/* void removing the last element, coredump */
it = mAppVec.erase(it);
} else {
it++;
}
}
}
void EnterApp(CApp *pApp)
{
pApp->ShowLogo();
}
void ShowAppList()
{
vector<CApp *>::iterator it;
cout << "App List: ";
for (it = mAppVec.begin(); it != mAppVec.end(); ) {
cout << (*it)->GetName() << " ";
it++;
}
cout << endl;
}
private:
string mName;
vector <CApp*> mAppVec;
};
class CHonor30Pro : public CPhone
{
public:
explicit CHonor30Pro(string name)
{
mName = name;
}
~CHonor30Pro()
{
}
void InstallApp(CApp *pApp)
{
pApp->RunPlatform(mName);
mAppVec.push_back(pApp);
}
void Uninstall(CApp *pApp)
{
vector<CApp *>::iterator it;
for (it = mAppVec.begin(); it != mAppVec.end(); ) {
if ((*it)->GetName() == pApp->GetName()) {
cout << "Uninstall " << (*it)->GetName() << " Success!" << endl;
/* void removing the last element, coredump */
it = mAppVec.erase(it);
} else {
it++;
}
}
}
void EnterApp(CApp *pApp)
{
pApp->ShowLogo();
}
void ShowAppList()
{
vector<CApp *>::iterator it;
cout << "App List: ";
for (it = mAppVec.begin(); it != mAppVec.end(); ) {
cout << (*it)->GetName() << " ";
it++;
}
cout << endl;
}
private:
string mName;
vector <CApp*> mAppVec;
};
int main(int argc, char *argv[])
{
CHonorOfKings theHonorOfKings("Timi");
CWeChat theWeChat("Earth");
// 定制 Honor 30 Pro 上的王者荣耀和微信
CHonor30Pro thePhone("Honor 30 Pro");
cout << "--- Honor 30 Pro ---" << endl;
thePhone.InstallApp(&theHonorOfKings);
thePhone.EnterApp(&theHonorOfKings);
thePhone.InstallApp(&theWeChat);
thePhone.EnterApp(&theWeChat);
thePhone.ShowAppList();
thePhone.Uninstall(&theWeChat);
thePhone.ShowAppList();
cout << endl;
// 定制 Mate40 上的王者荣耀和微信
CMate40 theMate40("Mate40");
cout << "--- Mate40 ---" << endl;
theMate40.InstallApp(&theHonorOfKings);
theMate40.EnterApp(&theHonorOfKings);
theMate40.InstallApp(&theWeChat);
theMate40.EnterApp(&theWeChat);
theMate40.ShowAppList();
theMate40.Uninstall(&theWeChat);
theMate40.ShowAppList();
cout << endl;
return 0;
}
总结
-
桥接模式主要将抽象与实现分离。完成相同的部分耦合,差异化部分解耦。使代码灵活性更强,达到类实现部分的修改不会影响到其他代码改动的效果。
-
客户端代码仅能够与高层抽象部分交互,不会接触到具体实现的详细细节。
-
履行开闭原则。新增抽象部分与实现部分,两者互不影响。
-
履行单一原则。抽象部分专注于处理高层逻辑,实现部分处理差异化细节。