C++设计模式_07_Bridge 桥模式

文章目录

  • [1. 动机(Motivation)](#1. 动机(Motivation))
  • [2. 代码演示Bridge 桥模式](#2. 代码演示Bridge 桥模式)
    • [2.1 基于继承的常规思维处理](#2.1 基于继承的常规思维处理)
    • [2.2 基于组合关系的重构优化](#2.2 基于组合关系的重构优化)
    • [2.3 采用Bridge 桥模式的实现](#2.3 采用Bridge 桥模式的实现)
  • [3. 模式定义](#3. 模式定义)
  • [4. 结构(Structure)](#4. 结构(Structure))
  • [5. 要点总结](#5. 要点总结)

与上篇介绍的Decorator 装饰模式一样,Bridge 桥模式也属于典型的"单一职责"模式,在特性上也与Decorator 装饰模式存在很多类似,但也存在不同得到地方。

"单一职责"模式的主要特征为:"在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。"

1. 动机(Motivation)

  • 由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。

  • 如何应对这种"多维度的变化"?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?

2. 代码演示Bridge 桥模式

针对上面的理解还是需要回到代码进行理解。

2.1 基于继承的常规思维处理

下面的代码是一个简单的通信模块,实现的功能包括Login()、SendMessage()...等。

cpp 复制代码
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平台的设计,但是由于平台的不同,需要override PlaySound()、DrawShape()、WriteText()、Connect()

cpp 复制代码
class PCMessagerBase : public Messager{
public:
    
    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};

类似PC平台的设计,Mobile上的设计也需要对相应的功能override

cpp 复制代码
class MobileMessagerBase : public Messager{
public:
    
    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};

另外我们可能会有在不同平台出"精简版"、"完美版"的需求,例如,在PC平台中class PCMessagerLite : public PCMessagerBase,其中实现了某些功能,而在class PCMessagerPerfect : public PCMessagerBase,这个时候"完美版"在实现功能时会有更多的功能内容,但是最基本的是一样,都是调用PCMessagerBase的方法。同样在Mobile平台也是有"精简版"、"完美版"的需求,虽然平台不同,但是业务功能需求也是一样的。

cpp 复制代码
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();
        //........
    }
};

整体代码如下:

cpp 复制代码
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 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();
}

以上是一种编译式装配的方式,上面代码的框架不难画出,得到如下图:

2.2 基于组合关系的重构优化

如果将平台实现列为n的话,将业务抽象列为m的话,整体类的数目就是1+n+m*n,此处不像Decorator 装饰模式中Lite和Perfect会有组合的情况,这样就造成大量的类,并且在类中的方法也是存在重复的例如:

PCMessagerPerfect中的:

cpp 复制代码
    virtual void Login(string username, string password){
        
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::Connect();
        //........
    }

MobileMessagerPerfect中的:

cpp 复制代码
    virtual void Login(string username, string password){
        
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::Connect();
        //........
    }

上面的内容都是似曾相识的重复,这种重复也是结构性的重复:

以下内容都是相同的

cpp 复制代码
//********
//........

不同的就是:PCMessagerBase::PlaySound();MobileMessagerBase::PlaySound();PCMessagerBase::Connect();MobileMessagerBase::Connect();

如果有Decorator 装饰模式基础的话可以发现可以使用继承转组合进行重构,例如将class PCMessagerPerfect : public PCMessagerBase { }中的父类变为数据成员class PCMessagerPerfect { PCMessagerBase* messager },声明为指针是因为指针具有多态性,相应的Mobile平台的也进行修改。

cpp 复制代码
class PCMessagerPerfect  {

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

class MobileMessagerPerfect {
    
    MobileMessagerBase* 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();
        //........
    }
};

从上面的代码可以看到,两个类不同的地方就是PCMessagerBase* messager;MobileMessagerBase* messager;,他们两个来自于一个基类,因此可以改写为以下形式:

cpp 复制代码
class PCMessagerPerfect  {

     Messager* messager; //未来运行时可以new PCMessagerPerfect();
    
    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();
        //........
    }
};

class MobileMessagerPerfect {
    
    Messager* messager; //未来运行时可以new MobileMessagerPerfect();
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();
        //........
    }
};

此时class PCMessagerPerfect和class PCMessagerPerfect已经没有什么区别了,他们的区别就是在未来运行时,编译时的代码就可以变为:

cpp 复制代码
class MessagerPerfect{

     Messager* messager; //未来运行时可以new PCMessagerPerfect()等;
    
    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();
        //........
    }
};

class MessagerPerfect{
    
    Messager* messager; //未来运行时可以new MobileMessagerPerfect();
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();
        //........
    }
};

这个时候发现两个类一模一样,这个时候只需要保留一个即可,同样的MessagerLite也是如法炮制,变为如下形式:

cpp 复制代码
class MessagerLite{

     Messager* messager; //未来运行时可以new PCMessagerLite()等;
    
    public:
    
    virtual void Login(string username, string password){
     
        messager->Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        messager->WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
       messager->DrawShape();
        //........
    }
};

此时整体代码变为如下:

cpp 复制代码
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 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 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();
        //........
    }
};

class MessagerPerfect{

     Messager* messager; //未来运行时可以new PCMessagerPerfect()等;
    
    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();
        //........
    }
};

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

此时可以发现 Messager* messager; //未来运行时可以new PCMessagerPerfect()等;是不成立的,这是因为class PCMessagerBase是一个抽象类。

为什么说其是抽象类呢? 因为其只override了class Messager中的部分虚函数,另外一些没有override。这个问题如何解决呢?在子类中只使用到了基类class Messager的一部分函数,里面的两个部分的函数塞在一起是不合适的,应该将其拆分开。

cpp 复制代码
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(){}
};

//平台实现 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(){
        //==========
    }
};



//业务抽象 m

//类的数目:1+n+m

class MessagerLite :public Messager {

    Messager* messager; //未来运行时可以new PCMessagerLite()等;
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 {
   Messager* messager; //未来运行时可以new PCMessagerPerfect()等; 
   
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();
        //........
    }
};

2.3 采用Bridge 桥模式的实现

上面也是继承转组合,当做到这里已经接近完美,同样的马丁福乐重构中讲到,如果同样的子类中有通用的字段,此处即为class MessagerLite :public Messagerclass MessagerPerfect :public Messager中的Messager* messager;,那么往上提到父类Messager中,变为以下形式:

cpp 复制代码
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(){}
};


//平台实现 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(){
        //==========
    }
};



//业务抽象 m

//类的数目: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(){
    //运行时装配
    MessagerImp* mImp=new PCMessagerImp();
    Messager *m =new Messager(mImp);
}

当然需要父类提供构造函数,去初始化MessagerImp* messagerImp;字段,子类放一个构造函数去调用父类的构造函数即可,此处未写。

此时就比较完美,类的个数就变为1+n+m,但是运行时还会有n*m的功能,这里就是桥模式。

回过头来看,在第一个版本中放了不同的函数,有不同的变化方向,一个变化方向是平台实现(PC、Mobile),一个变化方向是业务抽象(Lite、Perfect),这两个不同的变化方向,带动的行为的多态的实现也应该是往不同的方向走,也就不应该放在一个类中。

这也就体现了动机(Motivation) 中提到的多维度的变化

3. 模式定义

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

​ ------《设计模式》GoF

4. 结构(Structure)

上图是《设计模式》GoF中定义的Bridge 桥模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。

两个变化方向独立变化,而不是搅在一起变化。

5. 要点总结

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

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

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

相关推荐
风清扬_jd3 分钟前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
liu_chunhai11 分钟前
设计模式(3)builder
java·开发语言·设计模式
刷帅耍帅14 分钟前
设计模式-策略模式
设计模式·策略模式
冷白白21 分钟前
【C++】C++对象初探及友元
c语言·开发语言·c++·算法
睡觉然后上课39 分钟前
c基础面试题
c语言·开发语言·c++·面试
qing_0406031 小时前
C++——继承
开发语言·c++·继承
ya888g1 小时前
GESP C++四级样题卷
java·c++·算法
小叶学C++1 小时前
【C++】类与对象(下)
java·开发语言·c++
NuyoahC1 小时前
算法笔记(十一)——优先级队列(堆)
c++·笔记·算法·优先级队列
FL16238631292 小时前
[C++]使用纯opencv部署yolov11-pose姿态估计onnx模型
c++·opencv·yolo