设计模式·桥模式

 

当一个类有多个变化维度时,按照变化维度拆分此类,再把拆分的类以组合的方式加入

当一个类有多个变化维度时,按照变化维度拆分此类,再把拆分的类以组合的方式加入

缘起 Motivation

  • 由于某些类型的固有的实现逻辑
    • 使得它们具有两个变化的维度
    • 乃至多个纬度的变化
  • 如何应对这种“多维度的变化”?
  • 如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?

初级设计

  • 设计一个msg通讯模块
    • 处理用户的发送/接收消息
    • 播放声音
    • 绘制图像
    • 连接网络
    • 接收/显示文本
  • 支持不同的平台
    • PC平台
    • mobile平台

消息类

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

支持不同平台

支持PC平台和移动平台

  • PC平台

    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(){ //========== }
    };
    
  • 因为不同平台功能的实现是不同的

    • 所以要继承Messager再各自重写

支持不同的业务功能

  • PC端
    • 支持精减版
    • 支持完美版
  • 移动端
    • 支持精减版
    • 支持完美版

PC端支持的业务版本

  • 精减版

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

使用

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

类图

  • 子类数量非常多

重构

代码重复

  • PC和移动的业务版本代码除了PlaySound()Connect()各自实现不同以外,其他代码都一致
  • PC业务版本

    ```cpp class PCMessagerPerfect : public PCMessagerBase { public: virtual void Login(string username, string password){ PCMessagerBase::PlaySound(); //**** [与Mobile Perfect相同的代码] PCMessagerBase::Connect(); //…….. [与Moblie Perfect相同的代码] } }

  • 移动业务版本

    class MobileMessagerPerfect : public MobileMessagerBase {
    public:
        virtual void Login(string username, string password){
            MobileMessagerBase::PlaySound();
            //******** [与PC Perfect相同的代码]
            MobileMessagerBase::Connect();
            //........ [与PC Perfect相同的代码]
        }
    }
    

将继承变为组合

  • 重构前使用继承

    class PCMessagerLite : public PCMessagerBase {
    public:
        virtual void Login(string username, string password){
            PCMessagerBase::Connect();
            //........
        }
    }
    
  • 使用组合父类重构

    • PC平台

      class PCMessagerLite {
        PcMessagerBase *messager; // 只有父类/基类指针才具有多态性
      public:
          virtual void Login(string username, string password){
              messager->Connect();
              //........
          }
      
    • 移动平台

      class MobileMessagerLite {
        MobileMessagerBase *messager; // 只有父类/基类指针才具有多态性
      public:
          virtual void Login(string username, string password){
              messager->Connect();
              //........
          }
      
  • 再抽象更高层到基类:组合基类

    • PC平台 PcMessagerBase 抽象到 Messager

      class PCMessagerLite {
        Messager *messager; // 基类指针多态性 new PcMessagerBase()
      public:
          virtual void Login(string username, string password){
              messager->Connect();
              //........
          }
      
    • 移动平台 MobileMessagerBase 抽象到 Messager

      class MobileMessagerLite {
        Messager *messager; // 基类指针多态性 new MobileMessagerBase()
      public:
          virtual void Login(string username, string password){
              messager->Connect();
              //........
          }
      
    • 合并上面两个变化

      class MessagerLite {
            Messager *messager; // 基类指针多态性 
      
      public:
          virtual void Login(string username, string password){
              messager->Connect();
              //........
          }
          virtual void SendMessage(string message){
              messager->WriteText();
              //........
          }
          virtual void SendPicture(Image image){
              messager->DrawShape();
              //........
          }
      };
      
      • 以上精减了子类数量
  • 再优化
    • 因为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 void PlaySound()=0;
          virtual void DrawShape()=0;
          virtual void WriteText()=0;
          virtual void Connect()=0;
              
          virtual ~Messager(){}
      };
      
    • 所以继承了Messager类的PcMessagerBase也是一个虚基类

      class PCMessagerBase : public Messager{
      public:
          virtual void PlaySound(){ //********** }
          virtual void DrawShape(){ //********** }
          virtual void WriteText(){ //********** }
          virtual void Connect(){ //********** }
      };
      
      • 不能执行 Messager *messager = new PCMessagerBase()
      • 因为PCMessagerBase只是部分实现了Messager的虚函数
        • 所以自己还是一个虚类
      • 说明Messager不应该这么设计
    • 同理,如果MessagerLite类继承Messager也会是一个虚类

      class MessagerLite: Messager {
      }
      
      • MessagerLite也只会实现部分的Messager虚函数,导致自己也会只是一个虚类
      • 所以Messager中的接口应该拆分
    • 拆分Messager接口

      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;
              
          virtual ~MessagerImp(){}
      };
      
    • MessagerXXXX重新继承

      • MessagerLite

        class MessagerLite :public Messager {
           ; // [类中有与Perfect同样的字段,可以提升到父类]
        public:
            virtual void Login(string username, string password){
                messagerImp->Connect();
                //........
            }
            virtual void SendMessage(string message){
                messagerImp->WriteText();
                //........
            }
            virtual void SendPicture(Image image){
                messagerImp->DrawShape();
                //........
            }
        }
        
      • MessagerPerfect

        class MessagerPerfect  :public Messager {
          MessagerImp *messagerImp;  // [类中有与Lite同样的字段,可以提升到父类]
        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(){
        //运行时装配(组合)
        MessagerImp* mImp=new PCMessagerImp();
        Messager *m =new Messager(mImp);
    }
    

    其中

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

    此时类的个数是m+n,但支行时可以是m*n

  • 总结
    • 拆分Messager之前
      • Messager中的两类函数是不同的变化方向
        • 一个方向:平台实现
          • PC
          • Mobile
        • 一个方向:业务实现
          • perfect
          • lite

模式定义

将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化

结构

  • 各有各的变化,子类就是变化
    • 不同变化不会纠缠在一个基类中

要点总结

  • Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系
    • 组合关系
      • Message类中加入了一个抽象的指针 MessagerImp* messagerImp;
    • 使得抽象和实现可以沿着各自的维度来变化
      • 所谓抽象和实现沿着各自纬度的变化,即“子类化”它们
  • Bridge模式有时候类似于多继承方案
    • 但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差
    • Bridge模式是比多继承方案更好的解决方法
  • Bridge模式的应用一般在“两个非常强的变化维度”
    • 有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式