当一种抽象有多种实现时通常用继承来实现他们。桥模式的设计和装饰模式 有一定的相似之处,都是通过组合来解决继承的滥用问题。现有一个需求,将一个项目分成普通用户版和VIP版,该项目要同时支持WINDOW10 和手机版本。VIP版在当登录,发送消息时会有铃声提示和播放些动画,而普通用户版没有。基础功能抽象类如下:
class Base {
public:
virtual void login(string userid,string password)=0;
virtual void sendMessage(string message)=0;
virtual void playSound()=0;
virtual void playAnimation()=0;
}
在上述抽象类的设计中,由于 Windows10 和 手机版本的接口不一样,继承Base 类需要实现 playSound 和 playAnimation。而vip版和用户版是功能的扩展需实现login和sendMessage
class Base {
public:
virtual void login(string userid,string password)=0;
virtual void sendMessage(string message)=0;
virtual void playSound()=0;
virtual void playAnimation()=0;
};
class Windows10Base : public Base{
public :
virtual void playSound(){
//电脑版播放声音
}
virtual void playAnimation(){
//电脑版播放动画
}
virtual void login(string userid,string pwd){
// login()
}
virtual void sendMessage(string userid,string pwd){
// sendMessage()
}
};
class MobileBase : public Base{
public :
virtual void playSound(){
//手机版播放声音
}
virtual void playAnimation(){
//手机版播放动画
}
virtual void login(string userid,string pwd){
// login()
}
virtual void sendMessage(string userid,string pwd){
// sendMessage()
}
};
class Windows10VIP : public Windows10Base {
public :
virtual void login(string userid,string pwd){
Windows10Base::playSound();
Windows10Base::playAnimation();
// login()
}
virtual void sendMessage(string userid,string pwd){
Windows10Base::playSound();
Windows10Base::playAnimation();
// sendMessage()
}
};
class MobileVIP : public MobileBase {
public :
virtual void login(string userid,string pwd){
MobileBase ::playSound();
MobileBase ::playAnimation();
// login()
}
virtual void sendMessage(string userid,string pwd){
MobileBase ::playSound();
MobileBase ::playAnimation();
// sendMessage()
}
};
此设计方式是不利于扩展的,当前情况是支持两个系统的功能,用户等级分为两个就要有 4个派生类。假如业务将Vip等级进行细化,细化成 VIP1,VIP2,VIP3 等,那么每个等级不得同时去继承不同系统的类吗。假如需要支持Window 7 ,Window10,手机等 n 个不同的系统,业务要求用户等级需细分成 m 个不同的等级,按照上述的方法就会建立 m* n 个派生类。
如果应用桥模式,那么只需要新建 m+ n 个派生类,当进行系统扩展时只需要新增1个类,当VIP等级进行扩展时,也只需要新增1个类就行。
桥模式,将抽象部分与它的实现部分分离。反应出来的设计思想是,当抽象类中的纯虚函数应按照不同维度进行分离,分别继承,通过内成员指针将不同维度的方法关联在一起。换句话说,上述的需求分成 “可支持的系统”维度 和 “VIP等级” 维度。这两个不同维度中需实现的方法不一致时就不应该将这些抽象方法放一起。
class SystemBase {
public :
virtual void playSound()=0;
virtual void playAnimation()=0;
};
class UserLevelBase {
SystemBase * systemBase;
public :
UserLevelBase (s):systemBase(s){}
virtual void login(string userid,string pwd)=0;
virtual void sendMessage(string userid,string pwd)=0;
};
class NormalLevel :public UserLevelBase {
public :
NormalLevel (SystemBase *s):UserLevelBase (s){}
virtual void login(string userid,string pwd){
// login()
}
virtual void sendMessage(string userid,string pwd){
// sendMessage()
}
};
class VIPLevelfirst : public UserLevelBase {
public :
VIPLevelfirst (SystemBase *s):UserLevelBase (s){}
virtual void login(string userid,string pwd){
systemBase->playSound();
systemBase->playAnimation();
// login()
}
virtual void sendMessage(string userid,string pwd){
systemBase->playSound();
systemBase->playAnimation();
// sendMessage()
}
};
class MobileBase : public SystemBase{
public :
virtual void playSound(){
//手机版播放声音
}
virtual void playAnimation(){
//手机版播放动画
}
};
class Windows10Base : public SystemBase{
public :
virtual void playSound(){
//电脑版播放声音
}
virtual void playAnimation(){
//电脑版播放动画
}
};
//----------------------------------------
void main(){
SystemBase * mobileSystem = new MobileBase ();
SystemBase * PCSystem = new Windows10Base ();
VIPLevelfirst vipuserpc(PCSystem); //Windows10 的VIP用户
NormalLevel usermobile(PCSystem); //手机的普通用户
}
第一种设计模式不利于扩展,需要的派生类时不同维度功能的个数的乘积,而第二种将不同维度的抽象方法拆分成变成各自的抽象类,其需要的派生类是不同维度功能的个数的和。