• 设计模式---单一职责模式之桥模式(Bridge)


    一:概念

    Bridge模式又叫做桥接模式,其实基于类的最小设计原则,通过使用封装,聚合以及继承等行为来让不同的类承担不同的责任他的主要特点是吧抽象与行为实现分离开来,从而可以保持各部分的独立性以及一对他们的功能扩展

    二:动机

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

    三:代码讲解(通信)

    和Decorator模式非常像,所以我们需要注意区别

    (一)原代码

    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,Mobile,主要不同是在平台实现上,播放声音,图片绘制,端口写入数据,连接网络上不同。下面的不同注释方式代表不同的实现
    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(){
            //==========
        }
    };
    
    
    
    //业务抽象:在每一种的平台上,我们还要实现不同的功能,比如:经典版,完美版
    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(){
            //编译时装配
            Messager *m =
                new MobileMessagerPerfect();
    }

    编译时装配:(我们查看Decorator模式中运行时装配)

    我们编译时就已经完成了所有的等操作。不同于运行时装配可以通过组合来实现多种方式
    我们将平台实现设为n
    业务规模设置为m
    则上面的最终规模是
          1
    +n+n*m
    没有组合的情况,但是也是十分恐怖的操作。例如5个平台6个业务就需要36个类

    (二)改进业务抽象由继承转组合

    class PCMessagerLite{
        PCMessagerBase* 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();
            //........
        }
    };
    class MobileMessagerLite{
        MobileMessagerBase* 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();
            //........
        }
    };
    我们发现除了红色部分不同,但是由Decorator模式中知道当一个变量的声明类型都是某个类的子类的时候,我们就该将他声明为某个类(基类),由于多态,我们可以使得他在未来(运行时)成为子类

    (三)改进将子类转基类,实现在未来实现子类

    class PCMessagerLite{
        Messager* messager;    //在未来是PCMessagerBase
    public:
        
        virtual void Login(string username, string password){
            
            Messager->Connect();
            //........
        }
        virtual void SendMessage(string message){
            
            Messager->WriteText();
            //........
        }
        virtual void SendPicture(Image image){
            
            Messager->DrawShape();
            //........
        }
    };
    class MobileMessagerLite{
        Messager* messager;//在未来是MobileMessagerBase
    public:
        
        virtual void Login(string username, string password){
            
            messager->Connect();
            //........
        }
        virtual void SendMessage(string message){
            
            messager->WriteText();
            //........
        }
        virtual void SendPicture(Image image){
            
            messager->DrawShape();
            //........
        }
    };

    (四)发现两个类只是类名不同所以我们可以去重复

    class MessagerLite{
        Messager* messager;//在未来是MobileMessagerBase,和PcMessageBase
    public:
        
        virtual void Login(string username, string password){
            
            messager->Connect();
            //........
        }
        virtual void SendMessage(string message){
            
            messager->WriteText();
            //........
        }
        virtual void SendPicture(Image image){
            
            messager->DrawShape();
            //........
        }
    };
    class MessagerPerfect{  //一样的做法
        Messager* Messager;//在未来是MobileMessagerBase,和PcMessageBase
    public:
        
        virtual void Login(string username, string password){
            
            Messager->PlaySound();
            //********
            Messager->Connect();
            //........
        }
        virtual void SendMessage(string message){
            
            Messager->PlaySound();
            //********
            PCMessagerBase::WriteText();
            //........
        }
        virtual void SendPicture(Image image){
            
            Messager->PlaySound();
            //********
            Messager->DrawShape();
            //........
        }
    };

    (五)发现问题:接口规范出现问题(举一例)

    class PCMessagerLite{
        Messager* messager;    //=new PCMessagerBase()
    public:
        
        virtual void Login(string username, string password){
            
            Messager->Connect();
            //........
        }
        virtual void SendMessage(string message){
            
            Messager->WriteText();
            //........
        }
        virtual void SendPicture(Image image){
            
            Messager->DrawShape();
            //........
        }
    };

    1.在红一处:我们未来会使用到new PCMessagerBase()来实现,但是我们发现 平台实现是继承纯虚基类

    class PCMessagerBase : public Messager{  //只overwrite了部分纯虚函数,另外一部分并没有实现,所以还是抽象类无法实例化
    public:
        
        virtual void PlaySound(){
            //**********
        }
        virtual void DrawShape(){
            //**********
        }
        virtual void WriteText(){
            //**********
        }
        virtual void Connect(){
            //**********
        }
    };

    2.在红二处,我们并没有继承任何Messager基类,为何virtual使用,即使继承了,也是同上一样只实现了部分纯虚函数,无法进行实例化

    (六)我们发现上面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 ~Messager(){}
    };
    
    class MessagerImp{    //平台实现时使用
    public:
        virtual void PlaySound()=0;
        virtual void DrawShape()=0;
        virtual void WriteText()=0;
        virtual void Connect()=0;
        
        virtual ~MessagerImp(){}
    };

    下面是是修改基类

    //平台实现
    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{ MessagerImp* 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(); //........ } }; class MessagerPerfect:public Messager{  //一样的做法 MessagerImp* messager; public: virtual void Login(string username, string password){ messager->PlaySound(); //******** messager->Connect(); //........ } virtual void SendMessage(string message){ messager->PlaySound(); //******** messager->WriteText(); //........ } virtual void SendPicture(Image image){ messager->PlaySound(); //******** messager->DrawShape(); //........ } };

    (七)同样的字段向上提,同Decorator

    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(){}
    };
    void Process(){
        //运行时装配
        MessagerImp* mImp=new PCMessagerImp();
        Messager *m =new Messager(mImp);
    }
    此时类的数目是    1+1+n+m
    但是运行时还是有组合的功能

    (八)总结

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

    对比

    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(){}
    };
    版本一中放了不同的函数
        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;
    他们是不同的变化方向,一个方向是我们的平台实现,一个方向是业务抽象。
    两个不同的变化方向,带动的行为的多态的实现也是应该往两个不同的方向走

    四:模式定义

    将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。
    
                                                                ——《设计模式》GoF

    五:类图(结构)

    注意:稳定区域有一个多态指针在Messager中指向MessagerImp,这是和Decorator模式的一个区别
    将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。

    六:要点总结

    (一)Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固 有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓 抽象和实现沿着各自纬度的变化,即“子类化”它们。

    (二)Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。 Bridge模式是比多继承方案更好的解决方法。

    一个单继承其他地方用组合的方式

    (三)Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。

    当有多个变化维度Bridge模式都给我们提供了一个方向,就是把属于其他变化维度的东西,把它合到一起,打包成一个基类,然后用一个指针指向她,若是有多个变化维度,就将用多个指针指向她

    七:桥接模式和装饰模式的区别

    (一)通过类图

    (二)通过要点总结

    (三)收集资料

    1.定义:桥接模式就是应用合成/聚合复用原则的模式,而装饰模式是使用继承方式的模式。所以区别之一就出来了,一个使用的是继承方式,另一个使用的却是合成方式。

    2.角度:装饰模式是动态地添加一些额外功能的模式,也就是说装饰模式是适应新需求而添加新功能,并且不影响其他对象的一种模式;而桥接模式是适应变化维度的一种模式,它在于将对象的各个维度的变化都独立开来,使一些变化不受其他因素变化的影响。

     桥接模式是将各个维度的变化独立开来,扩展功能不多

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

    装饰模式,是为了更多适应新需求来扩展功能

     

    但是最终都有减少类的冗余,提高了复用性

    3.耦合:装饰模式中实现的是不同功能的子类进行封闭后独立的子类,但仍旧是紧耦合(因为是继承方式),而桥接模式中是将变化独立开,降低类之间的耦合度,尽最大可能实现松耦合(组合方式)。

    4.结构:桥接模式中是指将抽象与实现分离或属性与基于属性的行为进行分离;而装饰者只是对基于属性的行为进行封闭成独立的类。

    5. 行为:桥接中的行为是横向的行为,行为彼此之间无关联,具有非常强的变化维度;而装饰者模式中的行为具有可叠加性,其表现出来的结果是一个整体,一个各个行为组合后的一个结果

    八:案例实现(不同颜色图形绘制)

    (参考)设计模式读书笔记-----桥接模式

    (一)平台实现和抽象业务的划分

    class DrawShape
    {
    public:
        //获取红色,绿色,蓝色
        virtual void getRed() = 0;
        virtual void getGreen() = 0;
        virtual void getBlue() = 0;
    
        //画圆,矩形,正方形
        virtual void DrawCircle() = 0;
        virtual void DrawRect() = 0;
        virtual void DrawSquare() = 0;
    };
    我们的业务是画图形,我们的平台可以是各个颜色,所以我们将上面一个类分为两个不同类分别代表抽象业务和平台实现
    //平台实现基类
    class ColorImp
    {
    public:
        virtual void getPaint() = 0;
        virtual ~ColorImp(){}
    };
    
    //抽象业务基类
    class DrawShape
    {
    protected:
        ColorImp* color;
    public:
        DrawShape(ColorImp* clr) :color(clr){}
        virtual void getShape();
        virtual ~DrawShape(){}
    };

    (二)平台实现

    class ColorImp  //我们将获取颜色方法转换为基类,提供接口
    {
    public:
        virtual void getPaint() = 0;
    virtual ~ColorImp(){} };
    class ColorRed :public ColorImp { public: virtual void getPaint() { cout << "get red to paint" << endl; } }; class ColorGreen :public ColorImp { public: virtual void getPaint() { cout << "get green to paint" << endl; } }; class ColorBlue :public ColorImp { public: virtual void getPaint() { cout << "get blue to paint" << endl; } };

    (三)抽象业务实现

    class DrawShape
    {
    protected:
        ColorImp* color;
    public:
        DrawShape(ColorImp* clr) :color(clr){}
        virtual void getShape();
        virtual ~DrawShape(){}
    };
    
    class DrawCircle :public DrawShape
    {
    public:
        DrawCircle(ColorImp* clr) :DrawShape(clr){}
        virtual void getShape()
        {
            color->getPaint();
            cout << "use color to draw circle" << endl;
        }
    };
    
    class DrawRect :public DrawShape
    {
    public:
        DrawRect(ColorImp* clr) :DrawShape(clr){}
    
        virtual void getShape()
        {
            color->getPaint();
            cout << "use color to draw rectangle" << endl;
        }
    };
    
    class DrawSquare :public DrawShape
    {
    public:
        DrawSquare(ColorImp* clr) :DrawShape(clr){}
    
        virtual void getShape()
        {
            color->getPaint();
            cout << "use color to draw rectangle" << endl;
        }
    };

    (四)测试代码

    void main()
    {
        //获取平台
        ColorImp* color = new ColorRed();
        //设置业务
        DrawShape* shape = new DrawCircle(color);
        //测试结果
        shape->getShape();
    
        system("pause");
        return;
    }

    #include <iostream>
    using namespace std;
    
    class ColorImp
    {
    public:
        virtual void getPaint() = 0;
        virtual ~ColorImp(){}
    };
    
    class DrawShape
    {
    protected:
        ColorImp* color;
    public:
        DrawShape(ColorImp* clr) :color(clr){}
        virtual void getShape() = 0;
        virtual ~DrawShape(){}
    };
    
    
    class ColorRed :public ColorImp
    {
    public: 
        virtual void getPaint()
        {
            cout << "get red to paint" << endl;
        }
    };
    
    class ColorGreen :public ColorImp
    {
    public:
        virtual void getPaint()
        {
            cout << "get green to paint" << endl;
        }
    };
    
    class ColorBlue :public ColorImp
    {
    public:
        virtual void getPaint()
        {
            cout << "get blue to paint" << endl;
        }
    };
    
    
    class DrawCircle :public DrawShape
    {
    public:
        DrawCircle(ColorImp* clr) :DrawShape(clr){}
        virtual void getShape()
        {
            color->getPaint();
            cout << "use color to draw circle" << endl;
        }
    };
    
    class DrawRect :public DrawShape
    {
    public:
        DrawRect(ColorImp* clr) :DrawShape(clr){}
    
        virtual void getShape()
        {
            color->getPaint();
            cout << "use color to draw rectangle" << endl;
        }
    };
    
    class DrawSquare :public DrawShape
    {
    public:
        DrawSquare(ColorImp* clr) :DrawShape(clr){}
    
        virtual void getShape()
        {
            color->getPaint();
            cout << "use color to draw rectangle" << endl;
        }
    };
    
    void main()
    {
        //获取平台
        ColorImp* color = new ColorRed();
        //设置业务
        DrawShape* shape = new DrawCircle(color);
        //测试结果
        shape->getShape();
    
        system("pause");
        return;
    }
    全部代码
    个人觉得这种业务场景更加适合装饰器模式,因为这里平台扩展这些会更多些,但是用桥接也可以,还是需要大量经验才行....
  • 相关阅读:
    fork安全的gettid高效实现
    TCP_DEFER_ACCEPT的坑
    TCP Linger的坑
    Blade和其他构建工具有什么不同
    在Blade中结合gperftools检查内存泄露
    GraphViz web版
    用户场景分析
    java-二维数组——with 刘童格
    java-四则运算-五-网页版--with刘童格
    java-四则运算-四
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9533543.html
Copyright © 2020-2023  润新知