Bridge pattern (Bridge) – single responsibility class, structural model

Due to the inherent implementation logic of certain types, they have two dimensions of change, or even changes in multiple dimensions.

How to deal with this “multi-dimensional change”? How to use object-oriented technology to make types easily change in two or even multiple directions without introducing additional complexity.

In fact, I was stunned when I read the two sentences above.

ah? Is this also in multiple directions?

ah? Does this also solve the problem of too many subcategories?

ah? How is this different from a decorator?

Later I learned that Teacher Li Jianzhong meant something different from what I thought. The bridge mode solved the problem of too redundant classes. You may still remember the decorator example. Whether it is Buffer, Crypto, Network, or File, they rewrite the read, write, and seek functions. The core of the bridge model is that the direction in which your class is derived is completely inconsistent. What’s the meaning? The member functions in your base class cannot be placed in a class at all! What about the single responsibility that was agreed upon? Here’s an example

For example, I want to develop an instant messaging software. What functions does it have?

Login

SendMessage

Send PicturesSendMessage Of course there are other functions, but that’s it for now. (Send emojis, send documents, etc.)

besides:

PlaySound

Show ImageShowPictrue

Write copy (if you want to send a message) WriteText

Connect operation connect

You can see that the author divides these seven functions into two camps. The first three are business, face to face with users, what I want to accomplish, it is a collection of a series of actions. The last four are specific functions, which are the most basic support. To put it bluntly, the first three are a combination of the last four.

This class has two derivation directions, the first is platform support. That is, the last four, these are the basic functions. You need to adapt to the corresponding platform, such as PC, mobile, etc., then derive two classes. PCMessager and MobileMessage. The second derivation direction is business, simple version and deluxe version, just like the difference between TIM and QQ, with some functions reduced. good! That is, for PCMessager and MobileMessage, I have to derive PCMessagerLite, PCMessagerPerfect, MobileMessagerLite, and MobileMessagerPerfect. When I saw this, I exclaimed! Good guy! I know, decorators! A dime a dozen. But have you ever discovered that the two derived directions and rewritten functions have nothing to do with each other.

Post the original code

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(){}
};


//Platform implementation

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(){
        //==========
    }
};



//Business abstraction

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(){
        //Compile time assembly
        Messager*m=
            new MobileMessagerPerfect();
}


What to do,demolish it!

If you shouldn’t put it together, don’t put it there.

class Messager{
protected:
     MessagerImp* messagerImp;//...
public:
    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;
    
    virtualMessagerImp(){}
};


//Platform implementation n
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(){
        //==========
    }
};



//Business abstraction m

//Number of classes: 1 + n + m

class MessagerLite :public Messager {

    
public:
    
    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:
    
    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(){
    //Runtime assembly
    MessagerImp* mImp=new PCMessagerImp();
    Messager *m =new Messager(mImp);
}


MessagerImp is used for platform-level adaptation, and Messager is used for business. These are two directions. But, you need platform support to do business, right? So there is a messagerImp in Message. Let’s mention the base class directly, because all derived classes in the business direction are indispensable. If there are shared fields, then mention them.

The embodiment of the bridge of the bridge model is here (there is a messagerImp in Message)