设计模式之桥模式

171 阅读4分钟

某些类的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。例如:

class Messager{
public:
	/*
		下面三个函数是具体的业务层面的要求,比如实现用户登录功能,
		在用户登陆时发送消息和图片
	*/
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;
	/*
		下面四个函数是平台相关的,不同的平台对play、draw、write
		和connect有不一样的实现
	*/
    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;

    virtual ~Messager(){}
};

下面是PC和移动两个平台的实现类

class PCMessager : public Messager{
public:    
    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};

class MobileMessager : public Messager{
public:    
    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};

下面是PC和移动两个平台上具体的业务要求,比如两个平台都要做一个精简版和一个完美版。

class PCMessagerLite : public PCMessager{
public:    
    virtual void Login(string username, string password){        
        PCMessager::Connect();
        //........
    }
    virtual void SendMessage(string message){        
        PCMessager::WriteText();
        //........
    }
    virtual void SendPicture(Image image){        
        PCMessager::DrawShape();
        //........
    }
};

class PCMessagerPerfect : public PCMessager {
public:    
    virtual void Login(string username, string password){        
        PCMessager::PlaySound();
        //********
        PCMessager::Connect();
        //........
    }
    virtual void SendMessage(string message){        
        PCMessager::PlaySound();
        //********
        PCMessager::WriteText();
        //........
    }
    virtual void SendPicture(Image image){        
        PCMessager::PlaySound();
        //********
        PCMessager::DrawShape();
        //........
    }
};

class MobileMessagerLite : public MobileMessager {
public:    
    virtual void Login(string username, string password){        
        MobileMessager::Connect();
        //........
    }
    virtual void SendMessage(string message){        
        MobileMessager::WriteText();
        //........
    }
    virtual void SendPicture(Image image){        
        MobileMessager::DrawShape();
        //........
    }
};

class MobileMessagerPerfect : public MobileMessager {
public:    
    virtual void Login(string username, string password){        
        MobileMessager::PlaySound();
        //********
        MobileMessager::Connect();
        //........
    }
    virtual void SendMessage(string message){        
        MobileMessager::PlaySound();
        //********
        MobileMessager::WriteText();
        //........
    }
    virtual void SendPicture(Image image){        
        MobileMessager::PlaySound();
        //********
        MobileMessager::DrawShape();
        //........
    }
};

void Process(){
	//编译时装配
	Messager *m = new MobileMessagerPerfect();
}

从上面抽象类Messager可以看出,业务层面要求和平台实现相关其实是事务的两个方面,在类设计时不应该强行将它们放在一起,应该解耦它们之间固有的绑定关系,使得两者可以沿着各自的维度来变化。

/*
	平台实现类接口,具体的实现由各自平台完成
*/
class MessagerImp{
public:
    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;

    virtual MessagerImp(){}
};
/*
	业务要求类接口,引入平台实现类多态指针messagerImp,
	实现了晚绑定。其实不管是登陆还是发送消息还是发送图像,
	它们都依赖于具体的平台实现,而MessagerImp作为具体平台
	实现类的基类,扮演了“桥”的角色,因此自然而然地要在
	Messager类中引入messagerImp这个多态指针。
*/
class Messager{
protected:
    MessagerImp* messagerImp;//...
public:
	Messager(MessagerImp* mImp):messagerImp(mImp){

	}
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;

    virtual ~Messager(){}
};

/*具体平台实现类*/
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:
    MessagerLite(MessagerImp* mImp):Messager(mImp){

	}
    virtual void Login(string username, string password){        
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message){        
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){        
        messagerImp->DrawShape();
        //........
    }
};

class MessagerPerfect : public Messager {
public:
    MessagerPerfect(MessagerImp* mImp):Messager(mImp){

	}
    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 *mImp = new PCMessagerImp();
    Messager *m = new MessagerPerfect(mImp);
}

由上可见,不管是mobile还是PC,完美版的处理都有类MessagerPerfect来完成,精简版的处理都由类MessagerLite来完成,避免了在各自平台上分别实现这两种版本。 

不管是从上面的桥模式还是之前谈到的装饰器模式,可以通俗地总结出下面的原则。**如果动作要去处理对象,在动作和对象都存在多种情况时,比如动作(存在PC和移动两种实现方式),对象(存在精简和完美两种版本),那么最终设计出来的动作类要能同时处理各个种类的对象(比如PCMessagerImp能处理PC上面的完美和精简两种版本,MobileMessagerImp能处理移动平台上面的完美和精简两种版本),而不是为各种种类的对象分别设计各自的动作处理类。如何让动作类能同时处理各个种类的对象,这就需要利用各个对象共有的基类指针的多态性来实现。 **

上面讨论的是动作类同时处理各个种类的对象,那么反之也一样,即每个种类的对象也要能适应各个动作的处理,这时就利用各个动作共有的基类指针的多态性来实现了。