zoukankan      html  css  js  c++  java
  • 设计模式--桥模式

    考虑要设计一个Messager模块,这个模块要实现如下功能

    • 登录
    • 发送消息
    • 播放声音(登录或者播放的时候播放声音)
    • 画图(登录的时候显示的图片)

    那么最直观的设计如下

    class Messager {
    public:
      virtual void Login(string userName, string password) = 0;
      virtual void SendMessage(string message) = 0;
      
      virtual void PlaySound() = 0;
      virtual void DrawShape() = 0;
    
      virtual ~Message() {}
    };

    然后呢,我们这个Messager要实现跨平台的功能,要在PC平台和手机平台都能适用。然而对于PC和Mobile来说,唯一有区别的地方只有播放声音和画图这些与平台相关的函数不同,而登录、发送消息这些业务功能是相同的,所以你可能会设计出如下的两个类:

    class PCMessagerBase : public Messager {
      virtual void PlaySound() { /*******/ }
      virtual void DrawShape() { /*******/ }
    };
    class MobileMessagerBase : public Messager {
      virtual void PlaySound() { /*******/ }
      virtual void DrawShape() { /*******/ }
    };

    接下来继续考虑在同一个平台要推出两个不同版本的Messager,一个是精简版,一个是完美版。比如说精简版的在登录的时候不会播放声音和显示图片,而完美版的则支持这两个功能。其设计可能如下:

    class PCMessagerLite : public PCMessagerBase {
      virtual void Login(string userName, string password) { /******/ }
      virtual void SendMessage() { /*******/ }
    };
    class PCMessagerPerfect : public PCMessagerBase {
      virtual void Login(string userName, string password) {
        PCMessageBase::PlaySound();
        PCMessageBase::DrawShape();
      }
      virtual void SendMessage() {
        PCMessageBase::PlaySound();
        PCMessageBase::DrawShape();
      }
    }
    
    class MobileMessagerLite : public MobileMessagerBase {
      virtual void Login(string userName, string password) { /******/ }
      virtual void SendMessage() { /*******/ }
    };
    class MobileMessagerPerfect : public MobileMessagerBase {
      virtual void Login(string userName, string password) {
        MobileMessageBase::PlaySound();
        MobileMessageBase::DrawShape();
      }
      virtual void SendMessage() {
        MobileMessageBase::PlaySound();
        MobileMessageBase::DrawShape();
      }
    }

    那么到目前为止,类的结构图如下

    这么设计有什么缺点呢?缺点是代码重复性太大了。这种重复是一种结构性的重复。你仔细观察观察MobileMessagerPerfect和PCMessagerPeferct这两个类中Login和SendMessage的实现,会发现他们的流程是一模一样的,都是播放声音,显示登录画面,登录。不同的只是因为平台导致的播放声音和登录画面的实现不同。

    那么有什么改进的方法呢? 你可以发现在Perfect类的Login中的PlaySound和DrawShape可以追溯到同一个虚函数调用。如果你知道装饰者模式的话,那么你很快的可以想到改进的方法是将PCMobildBase作为一个类的字段去组合它,而不是去继承它

    class PCMessagePerfect {
    private:
      PCMessageBase* message;
    }

    通过message->PlaySound()来调用。同理MobildMessagePerfect中设计一个MobileMessageBase*字段。但是如果都申明为一个具体平台的Base字段的话,程序又写的太死了,所以你又发现可以将这个类的字段申明为Message*,然后在将来要构造Perfect对象的时候传进去一个具体的Base对象。但是现在问题又来了,现在*Base对象是个抽象类,是无法new出对象的。(Login, SendMessage等方法没有实现)

    那么问题的根源是什么?原因在是Message类中,你把业务逻辑(Login, SendMessage)和平台实现(PlaySound, DrawShap)混合到一起了,这样做是很不合适的,应该将他们拆分开。

    最终的类设计应该如下

    class Message { Login, SendMessage }  // 业务功能部分
    class MessageImp { PlaySound, DrawShap } // 平台实现部分
    class MessageBase : MessageImp {} // 具体的平台实现
    class MessagePerfect : Message {  // 继承Message的业务功能部分
      MessageImp* messageImp;      // 组合得到Message的平台功能部分
    }

     要支持PC平台的,只要传入一个PCMessageImp就可以了,而要实现手机平台的,只要传入一个MobileMessagerImp就好了。画一画类图,你还会发现总的类数量变少了。

  • 相关阅读:
    HTML标签,简单归纳
    表单<form></form>提交方式的区别
    Angular实战记录
    JavaScript常用编程问题记录
    Observable 的 Operators集合
    一些重要的学习资料
    周末
    vue2.0传值问题
    javascript日常学习小记
    web前端经典小题
  • 原文地址:https://www.cnblogs.com/David-Lin/p/10807168.html
Copyright © 2011-2022 走看看