今天学习了桥接模式:此模式可以解决因为设计不当的继承而导致的类爆炸问题,如下类图所示:
此类图的设计目的是为了给不同手机品牌各自设计不同的通讯录和游戏功能。但是可以看到,这样设计出来的代码可扩展性比较差。1)如果要再加另外一种手机品牌,那么就必须添加一个手机品牌类,再添加相应的游戏和通讯录类。 2)如果对每款手机都增加了一个新的功能如拍照,那么就得给每种手机品牌下增加一个新的拍照功能。
这两种情况都会导致类爆炸。
其实从这个类图可以看出一些类爆炸的端倪,我们知道继承必须是一种is a的关系,可是从此继承层次来看,明显通讯录和游戏不应该继承于手机品牌,是不合理的。
这是另外一个角度的不当设计:在不同的手机软件抽象类下去区分不同的手机品牌。这样不管是增加一个新的软件或者是增加一种新的手机品牌都需要添加较多的类。
我们可以从这两种不当的设计当中感觉到哪里有些问题:其实是因为这种设计将手机品牌和不同的软件杂糅在一起,这样的继承产生的类会以 品牌数 x 软件数 的方式增加,极易形成类的爆炸。
那么下面我们将手机品牌和手机软件来分开设计如下:
从这个类图可以看到,手机软件和手机品牌是分开来设计的,手机软件作为手机品牌的一个组成部分(组合关系)被手机品牌维护。这种设计方式,要增加一个手机品牌只需要重新继承一个新的品牌,要新增一款软件也只需要从手机软件类中派生一个新的子类。
合理设计的代码如下:
bridge.h
#ifndef _BRIDGE_H_
#define _BRIDGE_H_
#include <iostream>
using namespace std;
class PhoneSoft
{
public:
virtual void Run () = 0;
};
class PhoneGame : public PhoneSoft
{
public:
void Run () { cout << "PhoneGame:Run()" << endl; }
};
class PhoneContacts : public PhoneSoft
{
public:
void Run () { cout << "PhoneContacts:Run()" << endl; }
};
class PhoneBrand
{
public:
void SetPhoneSoft (PhoneSoft *soft) { pSoft = soft; }
virtual void Run () = 0;
protected:
PhoneSoft *pSoft; // 手机品牌持有软件对象指针(组合关系)
};
class PhoneBrandN : public PhoneBrand
{
public:
void Run() { pSoft->Run(); }
};
class PhoneBrandM : public PhoneBrand
{
public:
void Run() { pSoft->Run (); }
};
#endif
bridge.cpp
#include "bridge.h"
int main (void)
{
PhoneBrand *pBrand = new PhoneBrandN;//手机品牌
pBrand->SetPhoneSoft (new PhoneGame);//安装软件。此处安装的游戏软件都是相同的,如果要不同品牌安装不同的游戏软件该怎么处理?????
pBrand->Run(); // 运行软件
pBrand->SetPhoneSoft (new PhoneContacts);
pBrand->Run();
pBrand = new PhoneBrandM;
pBrand->SetPhoneSoft (new PhoneGame);
pBrand->Run();
pBrand->SetPhoneSoft (new PhoneContacts);
pBrand->Run();
return 0;
}
其实我觉的书上实现的这个例子有问题,比如如何体现不同品牌手机安装的游戏软件的差异性?当前的这种实现是不同的品牌安装的游戏软件都是相同的,那么如果不同品牌要安装不同的游戏软件,是否也需要从游戏类中派生出不同品牌的软件?