结构型设计模式之桥接模式【设计模式系列】

news/2024/12/22 15:06:04/

系列文章目录

C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程
设计模式系列

期待你的关注哦!!!
在这里插入图片描述

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

结构型设计模式之桥接模式

  • 系列文章目录
  • 一、桥接模式介绍
  • 二、桥接模式优缺点
    • 2.1 优点
    • 2.2 缺点
  • 三、桥接模式使用场景
  • 四、桥接模式实现
  • 五、桥接模式应用实例

一、桥接模式介绍

⚠️ 意图:
“将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”。这句话有三个关键词,也就是抽象化实现化脱耦

⚠️ 主要解决:
在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。

⚠️ 何时使用:
实现系统可能有多个角度分类,每一种角度都可能变化。

⚠️ 如何解决:
把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。将继承关系简化为组合关系。

桥接模式中的实现不是指抽象基类的具体子类对抽象基类中虚函数(接口)的实现,是指怎么去实现用户的需求。即在Implement具体类中实现Abstraction的接口功能,并且是通过组合(委托)的方式实现的,因此桥接模式中实现不是指的继承基类、实现基类接口,而是指的是通过对象组合实现用户的需求。
在这里插入图片描述

图1_1 桥接模式类图

桥接模式将继承关系转换为组合关系,从而降低了系统间的耦合,减少了代码编写量。使用组合(委托)的方式将抽象和实现彻底地解耦,好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。

将抽象部分与它的实现部分分离,使得它们可以独立地变化。抽象Abstraction与实现Implement分离,抽象部分Abstraction可以变化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp);实现部分Implement也可以独立变化,如new ConcreteImplementA()、new ConcreteImplementB()。

二、桥接模式优缺点

2.1 优点

  • 分离接口及其实现部分 。一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。将Abstraction与Implement分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类时,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要有这个性质。另外,接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道Abstraction和Implement即可。

  • 提高可扩充性 。可以独立地对Abstraction和Implement层次结构进行扩充。

  • 实现细节对客户透明 。可以对客户隐藏实现细节,例如共享Implement对象以及相应的引用计数机制。

  • 将可以共享的变化部分,抽离出来,减少了代码的重复信息。

  • 对象的具体实现可以更加灵活,可以满足多个因素变化的要求。

2.2 缺点

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

  • 客户必须知道选择哪一种类型的实现

三、桥接模式使用场景

  • 当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。如手机品牌有2种变化因素,一个是品牌,一个是功能。

  • 当多个变化因素在多个对象间共享时,考虑将变化的部分抽象出来再聚合或组合进来。

  • 当考虑一个对象的多个变化因素可以动态变化的时候,考虑使用桥接模式,如手机的手机品牌是变化的,手机的功能也是变化的,所以将每个可变化的因数分离出来,独立的变化。

  • 抽象工厂模式可以用来创建和配置一个特定的桥接模式。

  • 适配器模式用来帮助无关的类协同工作,通常在系统设计完成后才会被使用。然而,桥接模式则是在系统开始时就被使用,使得抽象接口和实现部分可以独立进行改变。

  • 桥接模式和装饰模式在一定程度上都是为了减少子类的数目,避免出现复杂的继承关系,但解决的方法却各有不同。装饰模式把子类中比基类中多出来的部分放到单独的类里面,以适应新功能增加的需要,当把描述新功能的类封装到基类的对象里面时,就得到所需要的子类对象,描述新功能的类通过组合可以实现很多的功能组合。 桥接模式则把原来的基类的实现化细节抽象出来,在构造到一个实现化的结构中,然后再把原来的基类改造成一个抽象化的等级结构,就可以实现系统在多个维度上的独立变化 。

四、桥接模式实现

Abstraction抽象基类:

#ifndef BRIDGE_ABSTRACTION_H
#define BRIDGE_ABSTRACTION_H#include "Implement.h"//抽象基类
class Abstraction {
public://需要实现的接口virtual void Request() = 0;protected:explicit Abstraction(Implement* imp):implement_(imp) {}protected:Implement *implement_;
};#endif //BRIDGE_ABSTRACTION_H

RefinedbstractionA具体类:

#ifndef BRIDGE_REFINEDBSTRACTIONA_H
#define BRIDGE_REFINEDBSTRACTIONA_H#include <iostream>
#include "Abstraction.h"
#include "Implement.h"using namespace std;class RefinedbstractionA : public Abstraction{
public:explicit RefinedbstractionA(Implement* imp) : Abstraction(imp){}~RefinedbstractionA() = default;void Request() override  {cout <<  "RefinedbstractionA::Request" << endl;//调用实现部分implement_->OperationImpl();}
};#endif //BRIDGE_REFINEDBSTRACTIONA_H

RefinedbstractionB具体类:

#ifndef BRIDGE_REFINEDBSTRACTIONB_H
#define BRIDGE_REFINEDBSTRACTIONB_H#include <iostream>
#include "Abstraction.h"
#include "Implement.h"using namespace std;class RefinedbstractionB : public Abstraction{
public:explicit RefinedbstractionB(Implement* imp) : Abstraction(imp){}~RefinedbstractionB() = default;void Request() override  {cout <<  "RefinedbstractionB::Request" << endl;//调用实现部分implement_->OperationImpl();}
};#endif //BRIDGE_REFINEDBSTRACTIONB_H

Implement抽象实现类:

#ifndef BRIDGE_IMPLEMENT_H
#define BRIDGE_IMPLEMENT_H//抽象实现类
class Implement {
protected:Implement() = default;~Implement() = default;public:virtual void OperationImpl() = 0;
};#endif //BRIDGE_IMPLEMENT_H

ConcreteImplementA具体实现类:

#ifndef BRIDGE_CONCRETEIMPLEMENTA_H
#define BRIDGE_CONCRETEIMPLEMENTA_H#include <iostream>
#include "Implement.h"using namespace std;
//具体实现类
class ConcreteImplementA : public Implement{
public:ConcreteImplementA() = default;~ConcreteImplementA() = default;//具体实现的功能函数void OperationImpl() override{cout <<  "ConcreteImplementA::OperationImpl" << endl;}
};#endif //BRIDGE_CONCRETEIMPLEMENTA_H

ConcreteImplementB具体实现类:

#ifndef BRIDGE_CONCRETEIMPLEMENTB_H
#define BRIDGE_CONCRETEIMPLEMENTB_H#include <iostream>
#include "Implement.h"using namespace std;
//具体实现类
class ConcreteImplementB : public Implement{
public:ConcreteImplementB() = default;~ConcreteImplementB() = default;//具体实现的功能函数void OperationImpl() override{cout <<  "ConcreteImplementB::OperationImpl" << endl;}
};#endif //BRIDGE_CONCRETEIMPLEMENTB_H

BridgeMain使用类:

#include <iostream>
#include "Abstraction.h"
#include "Implement.h"
#include "ConcreteImplementA.h"
#include "ConcreteImplementB.h"
#include "RefinedbstractionA.h"
#include "RefinedbstractionB.h"int main() {Implement* imp = new ConcreteImplementA();Abstraction* abs = new RefinedbstractionA(imp);abs->Request();Implement* imp1 = new ConcreteImplementB();Abstraction* abs1 = new RefinedbstractionB(imp1);abs1->Request();return 0;
}

将抽象部分与实现部分分离:实现系统可能有多角度(维度)分类,每一种分类都可能变化,把多种角度分离出来让它们独立变化,减少它们之间的耦合。

在发现需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时,要考虑用桥接模式。

组合/聚合复用原则:尽量使用组合/聚合,不要使用类继承。

优先使用对象的组合/聚合将有助于保持每个类被封装,并被集中在单个任务上。类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

五、桥接模式应用实例

电脑品牌和操作系统是两个概念,不同的品牌的电脑可以安装相同或不同的操作系统,两者都具有很大的变动性。如果单独以电脑品牌或操作系统为基类来进行继承扩展的话,会使类的数目剧增并且耦合性很高,如果更改电脑品牌或增加操作系统类型都会增加很多的变动。

将两者抽象出来两个基类分别是Computer和OS,在Computer类中聚合一个OS对象的基类将解决电脑品牌和操作系统扩展混乱的问题,两者的扩展就相对灵活,剪短了两者的必要联系。

Computer接口:

#ifndef COMPUTER_H
#define COMPUTER_Hclass OS;
class Computer
{
public:virtual void installOS(OS* os) = 0;
};#endif // COMPUTER_H

AppleComputer具体实现:

#ifndef APPLECOMPUTER_H
#define APPLECOMPUTER_H
#include "Computer.h"
#include "OS.h"class AppleComputer : public Computer
{
public:virtual void installOS(OS* os){cout << "AppleComputer ";os->installOS_Imp();}
};#endif // APPLECOMPUTER_H

ThinkPadComputer具体实现:

#ifndef THINKPADCOMPUTER_H
#define THINKPADCOMPUTER_H
#include "Computer.h"
#include "OS.h"class ThinkPadComputer : public Computer
{
public:virtual void installOS(OS* os){cout << "ThinkPadComputer ";os->installOS_Imp();}
};#endif // THINKPADCOMPUTER_H

OS接口:

#ifndef OS_H
#define OS_H
#include <iostream>
using namespace std;class OS
{
public:virtual void installOS_Imp() = 0;
};#endif // OS_H

LinuxOS具体实现:

#ifndef LINUXOS_H
#define LINUXOS_H
#include "OS.h"class LinuxOS : public OS
{
public:virtual void installOS_Imp(){cout << "has installed Linux OS" << endl;}
};#endif // LINUXOS_H

WindowsOS具体实现:

#ifndef WINDOWSOS_H
#define WINDOWSOS_H
#include "OS.h"class WindowsOS : public OS
{
public:virtual void installOS_Imp(){cout << "has installed Windows OS" << endl;}
};#endif // WINDOWSOS_H

http://www.ppmy.cn/news/971743.html

相关文章

企业网络安全合规框架体系

云安全联盟大中华区发布报告《企业网络安全合规框架体系》&#xff08;以下简称报告&#xff09;&#xff0c;该报告对典型业务场景给出了参考实例&#xff0c;供广大甲方单位、集成商、咨询机构参考。 近些年&#xff0c;随着国内网络安全领域相关法律、法规、政策文件、标准规…

Flink简介及部署模式

文章目录 1、Flink简介2、Flink部署2.1 本地模式2.1 Standalone模式部署2.2 Standalone模式下的高可用2.3 Yarn模式Yarn模式的高可用配置&#xff1a;yarn模式中三种子模式的区别&#xff1a; 3、并行度4、提交命令执行指定任务Application Mode VS yarn per-job 5、注意事项5、…

首次冲刺上市失败后,诺威健康CFO张子栋离职,TPG是控股股东

近日&#xff0c;贝多财经了解到&#xff0c;首次冲刺上市失败后&#xff0c;诺威健康科技控股有限公司&#xff08;下称“诺威健康”&#xff09;的首席财务官&#xff08;CFO&#xff09;张子栋已经离职。而这距离张子栋加入诺威健康&#xff0c;才刚满一年。 5月17日&#…

[安卓系统导航升级系统,车机升级系统]汽车系统升级更新,诺威达k2201升级包

汽车导航升级固件刷机包大屏导航升级救砖&#xff0c;车机使用卡&#xff0c;开机慢&#xff0c;加载音乐慢等问题&#xff0c;更新升级之后&#xff0c;系统也比之前流畅了&#xff0c;诺威达&#xff1a;K2001,K2001N,K2101,K2201,K2201S,K3001 备注&#xff1a;系统升级&…

《浪潮之巅》读书笔记

1. 评价一家上市公司的好坏&#xff0c;其实只要看那些最优秀的人是流进这家公司&#xff0c;还是流出这家公司即可。 2. 早期领导人的灵魂常常会永久地留在这家公司&#xff0c;即使他们已经离去。 3. 在每一次技术革命中&#xff0c;新技术必须比老的技术有数量级的进步才能站…

物联网传感器市场的六个特点分析

什么是传感器 传感器&#xff1a;传感器是一种检测装置&#xff0c;能感受到被测量的信息&#xff0c;并能将感受到信息变换信号或者其他所需形式进行输入去&#xff0c;以满足信息的收集、传输、存储、控制等要求。当然这都是官方的话语&#xff0c;看起来比较难理解。相比较…

全球及中国淫羊藿软胶囊行业销售状况及竞争前景预测报告(2022-2027)

全球及中国淫羊藿软胶囊行业销售状况及竞争前景预测报告(2022-2027) ================================================ 【报告编号】: BG417004 【出版时间】: 2022年2月 【出版机构】: 中智正业研究院 内容简介: 1 淫羊藿软胶囊市场概述 1.1 淫羊藿软胶囊行业概述及统计…

vue系统学习(持续更新)

vue学习 1、vue特点 1、采用组件化模式&#xff0c;提高代码的复用率&#xff0c;且让代码更好的维护。 2、声明式编码&#xff0c;让编码人员无需直接操作DOM&#xff0c;提高开发效率。 1、老的html写法 遍历一个Person数组数据 let htmlStr person.forEach(p>{ htm…