桥模式(Bridge)以及工程应用

人生如梦,一樽还酹江月


 

概念

建立两套相互独立的类继承体系,然后,通过让一个类引用另一个类来连结起来, 避免类组合的爆炸。

例如,当前有各种图形:圆形,三角形,长方形,它们有公共的基类class ShapeBase, 公共的虚函数接口为drawShape,用于绘制出不同的形状。

现在加一个需求,需要能绘制出不同颜色的形状,颜色要求有红色,绿色,蓝色,而且将来可能增加颜色。

如果用继承的方式来实现的话,一种方式是,有一个公共的ShapeBase基类,然后派生出各种图形,然后各个图形又派生出自己的各种颜色的图形,如下图所示,缺点是

特别容易爆炸。

 

于是桥模式登场了:

将图形和颜色分成两个独立的继承体系,基类分别为Class ShapeBase,以及 Class ColorBase, 在ShapeBase中增加一个ColorBase*指针,使用该指针

去调用color体系的方法,从而解耦。

 

void Circle::DrawShape(){

    paintCircle(); //绘制圆形

    mColorImplement->PaintShape();//上色

}

 

 


工程应用例子

场景:

赛车有多种动力源,Motor, Engine, HydraulicCynlinder, 构成一个继承体系;动力源的作用是接收车手的油门信号,并结合自己的内部算法逻辑,输出车的动力扭矩。

对于内部算法,有多种:SampleTorqueAlgorithm仅仅根据油门产生扭矩, GasRpmTorqueAlgorithm根据油门和转速产生扭矩, 构成一个继承体系。

这些算法可以被各种动力源使用,即动力源和算法构成组合。

可以使用桥模式,在每个动力源class的内部增加一个指针,指向其使用的算法示例,从而解耦。

 

代码结构:

有一些技巧,例如readConfig函数用于从外部配置文件中读入具体参数。请细看代码。

 

算法类的体系:

#pragma once
#include <iostream>
#include <memory>
using namespace std;

class TorqueAlgorithmBase
{
public:
    TorqueAlgorithmBase() {}
    virtual ~TorqueAlgorithmBase() {}
    virtual float solve(float gas, float rpm) = 0;
    virtual void readUserConfig(int fileId) = 0;//可以用于读入用户的配置文件内容,例如读入算法使用到的可配置数据。
};

class SampleTorqueAlgorithm :public TorqueAlgorithmBase
{
public:
    SampleTorqueAlgorithm() {}
    virtual ~SampleTorqueAlgorithm() {}
    virtual float solve(float gas, float rpm) override{
        return gas * mMaxOutput;
    }

    virtual void readUserConfig(int fileId) override{
        //读取针对该算法的用户提供的数据,例如mMaxOutput
    }
private:
    float mMaxOutput = 300.0f;
};

class GasRpmTorqueAlgorithm :public TorqueAlgorithmBase
{
public:
    GasRpmTorqueAlgorithm() {}
    virtual ~GasRpmTorqueAlgorithm() {}
    virtual float solve(float gas, float rpm) override {
        //根据gas和rpm计算输出的torque,例如,如下一个算法
        float res = gas * mMaxOutput + rpm * gas*0.001;
        return res;
    }

    virtual void readUserConfig(int fileId) override {
        //读取针对该算法的用户提供的数据,例如mMaxOutput
    }

private:
    float mMaxOutput = 300.0f;
};

动力源的类体系:

class EnergySourceBase {
public:
    EnergySourceBase() {
        //默认使用SampleTorqueAlgorithm
        mTorqueAlgo = std::make_shared<SampleTorqueAlgorithm>();//基类指针指向子类obj
    }
    virtual ~EnergySourceBase() {}

    virtual float generateTorque(float gas, float rpm) = 0;

    virtual void readUserConfig(int fileId)=0;//读入用户的配置,并根据该配置决定使用哪种TorqueAlgo.实际中入参可以是一个配置文件


protected:
    shared_ptr<TorqueAlgorithmBase> mTorqueAlgo;//基类指针
};

class Motor :public EnergySourceBase {
public:
    Motor(){}
    virtual ~Motor(){}
    virtual float generateTorque(float gas, float rpm) override {
        return  mTorqueAlgo->solve(gas, rpm);
    }

    virtual void readUserConfig(int fileId) override {
        //从fileId文件中读取出指示使用哪种torqueAlgo的信息,例如存到int order中。
        int order = 0;
        if (order == 0) {
            mTorqueAlgo.reset(new SampleTorqueAlgorithm());
        }
        else {
            mTorqueAlgo.reset(new GasRpmTorqueAlgorithm());
        }  // -- 以上代码在各个子类中有重复,可以整合到父类中。例如,可以在父类中加一个geneRateAlgo函数。

        //--从file中提取Motor的相关参数,例如重量。
        //weight=...

        //让mTorqueAlgo读取其需要的参数。
        mTorqueAlgo->readUserConfig(fileId);
    }
private:
    float weight;
};

class Engine :public EnergySourceBase {
public:
    Engine() {}
    virtual ~Engine() {}
    virtual float generateTorque(float gas, float rpm) override {
        return  mTorqueAlgo->solve(gas, rpm);
    }

    virtual void readUserConfig(int fileId) override {
        //从fileId文件中读取出指示使用哪种torqueAlgo的信息,例如存到int order中。
        int order = 0;
        if (order == 0) {
            mTorqueAlgo.reset(new SampleTorqueAlgorithm());
        }
        else {
            mTorqueAlgo.reset(new GasRpmTorqueAlgorithm());
        }
        //--从file中提取Engine的相关参数,例如重量。
        //weight=...

        //让mTorqueAlgo读取其需要的参数。
        mTorqueAlgo->readUserConfig(fileId);
    }

private:
    float weight;
};

class HydraulicCynlinder :public EnergySourceBase {
public:
    HydraulicCynlinder() {}
    virtual ~HydraulicCynlinder() {}
    virtual float generateTorque(float gas, float rpm) override {
        return  mTorqueAlgo->solve(gas, rpm);
    }

    virtual void readUserConfig(int fileId) override {
        //从fileId文件中读取出指示使用哪种torqueAlgo的信息,例如存到int order中。
        int order = 0;
        if (order == 0) {
            mTorqueAlgo.reset(new SampleTorqueAlgorithm());
        }
        else {
            mTorqueAlgo.reset(new GasRpmTorqueAlgorithm());
        }
        //--从file中提取HydraulicCynlinder的相关参数,例如重量,mLiquidDensity。
        //weight=...; mLiquidDensity=;

        //让mTorqueAlgo读取其需要的参数。
        mTorqueAlgo->readUserConfig(fileId);
    }
private:
    float weight;
    float mLiquidDensity;
};

Test it:

void main() {

    Engine obj;//客户需要一个Engine 

    int fileId = 3;
    obj.readUserConfig(fileId);//fileId代表一个文件,里面存储着engine的具体参数, 使用哪种torque ALgorithm信息,以及该种algorithm的具体参数。
    float gas = 0.3;
    float rpm = 3000;
    
    float torque = obj.generateTorque(gas, rpm);
}

Ref:

https://refactoring.guru/design-patterns/bridge

https://refactoring.guru/design-patterns/bridge/cpp/example

https://www.cnblogs.com/WindSun/p/10260547.html


最是人间留不住,朱颜辞镜花辞月。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

First Snowflakes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值