动机
由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个维度的变化 那么该如何应对这种多维度的变化?如何利用面向对象技术使得类型可以轻松沿着两个乃至多个方向变化?而不引入额外的复杂度。
桥模式定义
将抽象部分(业务功能)与实现部分(平台实现)分离,使得它们都可以独立地变化。 让我们直接上代码,分析一下没有使用桥模式下的代码如何难以独立变化。
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(); }
类图.JPG通过阅读上述代码,可以发现一个问题,平台的变化和版本的是两个维度的问题,如果平台增加,那么增加的平台下又增加多几个版本,就是不同的平台下面还分有若干版本,子类数量会增多。类的数量为1+n+n*m。
课程中提到桥模式,让我们一起来看看桥模式。
分析上述代码,
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.针对实现进行修改