C++设计模式:桥模式(五)

1、定义与动机
  • 桥模式定义:将抽象部分(业务功能)与实现部分(平台实现)分离,使他们可以独立地变化
  • 引入动机
    • 由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个维度的变化
    • 如何应对这种“多维度的变化”?如何利用面向对象技术来使用类型可以轻松地沿着两个乃至多个方向变化,二部引入额外的复杂度?
2、案例分析

假设现在存在这样一个需求:

  • 有一个消息发送器Messager,里面有七个方法
    • 大致需要完成登录、发送文本消息、发送图片消息、播放声音、建立连接等
    • 同时由于存在不同的终端设备:例如手机、电脑、pad等,他们的这些实现方式肯定会存在一定的不同
    • 并且对于同一种设备存在不同的功能组合问题:例如发送图片、消息之前播放声音等。
2.1、首先设计一个抽象接口

根据上面所提出的需求可以写出如下的接口代码,函数都是纯虚函数和虚函数组成

class Messager{
public:
    virtual void Login() = 0;
    virtual void SendMessage() = 0;
    virtual void SendPicture() = 0;

    virtual void PlaySound() = 0;
    virtual void DrawShape() = 0;
    virtual void WriteText() = 0;
    virtual void Connect() = 0;

    virtual ~Messager(){

    }
};
2.2、针对不同的终端平台编码设计
  • 例如针对PC、手机…平台设计它们的是如何发送文本、画图、播放声音、建立网络通信连接的代码
  • 但是这些Base类依然是一个抽象基类,因为存在一些纯虚函数没有实现,因此其本身也是一个抽象类
// PC平台实现(抽象类),没有完全实现完毕所有的纯虚函数
class PCMessagerBase: public Messager{
public:
    virtual void PlaySound(){
        // *****
    }
    virtual void DrawShape(){
        // *****
    }
    virtual void WriteText(){
        // *****
    }
    virtual void Connect(){
        // *****
    }
}

// Mobile平台实现(抽象类),没有完全实现完毕所有的纯虚函数
class MobileMessagerBase: public Messager{
public:
    virtual void PlaySound(){
        // *****
    }
    virtual void DrawShape(){
        // *****
    }
    virtual void WriteText(){
        // *****
    }
    virtual void Connect(){
        // *****
    }
}
2.3、针对同平台不同量级的实现
  • PCMessagerLite:轻量级实现,该实现类只完成最基本的功能,普通登录、发送图片文本消息
  • PCMessagerPerfect:完美级实现,在执行这些操作之前可以辅助带一些播放声音等额外的功能
class PCMessagerLite: public PCMessagerBase{
public:
    virtual void Login(){

        PCMessagerBase::Connect();
        // ******
    }
    virtual void SendMessage(){
        PCMessagerBase::WriteText();
        // ******
    }
    virtual void SendPicture(){
        PCMessagerBase::DrawShape();
        // ******
    }
};

class PCMessagerPerfect: public PCMessagerBase{
public:
    virtual void Login(){
        PCMessagerBase::PlaySound();
        PCMessagerBase::Connect();
        // ******
    }
    virtual void SendMessage(){
        PCMessagerBase::PlaySound();
        PCMessagerBase::WriteText();
        // ******
    }
    virtual void SendPicture(){
        PCMessagerBase::PlaySound();
        PCMessagerBase::DrawShape();
        // ******
    }
};


/* 

	MobileMessagerLite、MobileMessagerPerfect的实现如上
	略过
*/
2.4、分析
  • 很明显针对上面的代码一眼就能看出:存在大量的代码重复PCMessagerLite和MobileMessagerLite的代码区别完全就在于类名不同、继承的Base类不同

    • 这一问题很好解决:首先把继承变成组合,把继承的Base类组合到Lite和Perfect类中

    • 此时只是子类不同:由于子类都来自同一个基类Messager,那么可以使用多态,进行成员声明类型的改变。

    • 最后在使用时动态的传入所需要的不同平台的实现即可。

  • 这样实现会导致一个类的急剧膨胀,假设只有1个Messager的接口、n个Base类、那么不同量级的实现是n * m的,总体是一个 1 + n + n * m + …

  • 这一点和装饰器模式很像,不过装饰器模式是一个阶乘的爆炸,但这里的工作量也不低

3、桥模式

针对上面所发现的弊端进行改进,首先可以进行的改进

3.1、改进(一)
  • 将继承改成组合的方式,可以很容易的写出PCMessagerLite、MobileMessagerLite的实现代码
class PCMessagerLite{
private:
    PCMessagerBase* pcMessagerBase;
public:
    virtual void Login(){

        pcMessagerBase->Connect();
        // ******
    }
    virtual void SendMessage(){
        pcMessagerBase->WriteText();
        // ******
    }
    virtual void SendPicture(){
        pcMessagerBase->DrawShape();
        // ******
    }
};

class MobileMessagerLite{
private:
    MobileMessagerBase* mobileMessagerBase;
public:
    virtual void Login(){
        mobileMessagerBase->Connect();
        // ******
    }
    virtual void SendMessage(){
        mobileMessagerBase->WriteText();
        // ******
    }
    virtual void SendPicture(){
        mobileMessagerBase->DrawShape();
        // ******
    }
};
3.2、改进(二)
  • 然后其实可以将PCMessagerLite、MobileMessagerLite提炼成一个代码,这两份代码核心不点在于各自做组合的对象不同
  • 然而恰巧不巧的事:它们所组合的对象都来自同一个基类,因此可以提炼成MessagerLite代码
class MessagerLite{
private:
    Messager* messager;
public:
    MessagerLite(Messager *msg): messager(msg){
        
    }
    virtual void Login(){
        messager->Connect();
        // ******
    }
    virtual void SendMessage(){
        messager->WriteText();
        // ******
    }
    virtual void SendPicture(){
        messager->DrawShape();
        // ******
    }
};
  • 但是写完这个代码会有一个问题:基类PCMessagerBase和基类MobileMessagerBase是抽象类(没有完全实现所有的纯虚函数),而抽象类是无法实例化对象的,因此这个代码是有问题的,无法过编译。

  • 仔细分析问题核心所在点:

    • 一路下来,我们只在最后不同量级的代码编写实现Messager抽象类的login、SendMessage、SendPicture三个纯虚函数
    • 之前的那些Base基类并没有实现这三个方法才导致它们依然是一个抽象类
    • 所以问题的核心点:Messager抽象类的接口方法太多,官方一点的术语:职责不够单一(违背单一职责原则)
3.3、完美改进(三)
  • 为了解决单一职责问题,可以将Messager抽象接口的拆成两个抽象接口
  • Base类继承实现一个接口
  • 不同量级实现一个接口,然后组合Base类的指针(这里动态传入Base类不同的指针)
class Messager{
public:
    virtual void Login() = 0;
    virtual void SendMessage() = 0;
    virtual void SendPicture() = 0;

    virtual ~Messager(){

    }
};

class MessagerImpl{
    virtual void PlaySound() = 0;
    virtual void DrawShape() = 0;
    virtual void WriteText() = 0;
    virtual void Connect() = 0;
};

// PC平台实现
class PCMessagerBase: public MessagerImpl{
public:
    virtual void PlaySound(){
        // *****
    }
    virtual void DrawShape(){
        // *****
    }
    virtual void WriteText(){
        // *****
    }
    virtual void Connect(){
        // *****
    }
}

// Mobile平台实现
class MobileMessagerBase: public MessagerImpl{
public:
    virtual void PlaySound(){
        // *****
    }
    virtual void DrawShape(){
        // *****
    }
    virtual void WriteText(){
        // *****
    }
    virtual void Connect(){
        // *****
    }
}


class MessagerLite: public Messager{        // 实现抽象类
private:
    MessagerImpl* messagerImpl;             // 组合对象
public:
    MessagerLite(MessagerImpl *msgi): messagerImpl(msgi){

    }
    virtual void Login(){
        messager->Connect();
        // ******
    }
    virtual void SendMessage(){
        messager->WriteText();
        // ******
    }
    virtual void SendPicture(){
        messager->DrawShape();
        // ******
    }
};



class MessagerPerfect: public Messager{     // 实现抽象类
private:
    MessagerImpl* messagerImpl;             // 组合对象
public:
    MessagerPerfect(MessagerImpl *msgi): messagerImpl(msgi){

    }
    virtual void Login(){
        MobileMessagerBase::PlaySound();
        MobileMessagerBase::Connect();
        // ******
    }
    virtual void SendMessage(){
        MobileMessagerBase::PlaySound();
        MobileMessagerBase::WriteText();
        // ******
    }
    virtual void SendPicture(){
        MobileMessagerBase::PlaySound();
        MobileMessagerBase::DrawShape();
        // ******
    }
};
  • 这样就能完成所有的功能:其核心点在于使用继承 + 组合的模式取代单一的继承方式来实现所有的功能
  • 采用桥模式的设计方式将类的膨胀改写成:1 + n + m的形式,整整比普通模式少了至少一个数量级的代码
4、总结
  • Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,即“子类化”它们。
  • Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类存在多个变化的原因和方向),复用性比较差。Bridge模式是比多级车工方案更好的解决方案。
  • Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也可以有多余两个的变化维度,这时可以使用Bridge的扩展模式。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/525375.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2.k8s架构

目录 k8s集群架构 控制平面 kube-apiserver kube-scheduler etcd kube-controller-manager node 组件 kubelet kube-proxy 容器运行时(Container Runtime) cloud-controller-manager 相关概念 k8s集群架构 一个Kubernetes集群至少包含一个控制…

飞书API(3):Python 自动读取多维表所有分页数据的三种方法

上一小节介绍了怎么使用 Python 读取多维表的数据,看似可以成功获取到了所有的数据,但是在实际生产使用过程中,我们会发现,上一小节的代码并不能获取到所有的多维表数据,它只能获取一页,默认是第一页。因为…

MySQL的存储引擎、索引与事务

常见的端口号 MySQL–3306http–80https–443tcp–23fcp–21tomcat–8080ssh–22oracle–1521rockermq–9876 存储引擎 使用指令查看所有引擎: show engines;从图中可以看出MySQL默认的存储引擎是InnoDB;并且在5.7版本所有的存储引擎中只有 InnoDB 是…

亮数据----教你轻松获取数据

文章目录 1. 数据采集遇到的瓶颈1.1 不会造数据?1.2 不会写爬虫代码? 2.IP 代理基础知识2.1 基本概念2.2 作用和分类2.3 IP 代理的配置和使用2.4 安全和合规 3. 为何使用亮数据 IP 代理3.1 拥有丰富的代理网络服务3.2 简单易操作的采集工具3.3 拥有各平台…

路由器对数据包的处理过程分析笔记

虽然TCP-IP协议中传输数据会在各个路由器再次经过物理层、链路层、网络层的解封装、加工、封装、转发,但是对于两个主机间的运输层,在逻辑上,应用进程是直接通信的。 路由器主要工作在网络层,但它也涉及到物理层和链路层的一些功能…

PWM 脉宽跟随方案介绍

1. 前言 数字电源产品在使用桥式电路拓扑或是多路交错控制中,有时会需要滞后臂的 PWM 脉宽严格跟随超前臂的 PWM 脉宽,或从路的 PWM 脉宽严格跟随主路的 PWM 脉宽,本文将介绍如何利用高精度定时器实现 PWM 输出脉宽跟随,一种使用…

ai智能电销机器人的核心技术,工作原理和作用

科技快速发展的同时,带来了人工智能产品的普及。而ai智能电销机器人则成为推进电销行业的产物,那么ai智能电销机器人是如何帮助企业高效触客,有效地工作,效果又如何呢?我们一起来看看吧! 一、ai智能电销机器…

软件的生命周期_瀑布模型

瀑布模型 描述软件生成到消亡的过程模型图 该模型目前实际工作中已不常用,但是该模型是其他新型模型的“鼻祖 瀑布模型的优点 每个阶段比较清楚,并且有对应的文档产生 当前一个阶段完成后,才开始后面的阶段(一次性的&#xff09…

「媒体邀约」天津媒体邀约资源有哪些?媒体宣传现场报道

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 天津作为中国北方的重要城市,拥有丰富的媒体资源,可以为各类活动提供全面的媒体宣传和现场报道。以下是天津地区的媒体邀约资源: 1. 报纸媒体 - 《天…

如何搭建APP分发平台分发平台搭建教程

搭建一个APP分发平台可以帮助开发者更好地分发和管理他们的应用程序。下面是一个简要的教程,介绍如何搭建一个APP分发平台。 1.确定需求和功能:首先,确定你的APP分发平台的需求和功能。考虑以下几个方面: 用户注册和登录&#xff…

Redis从入门到精通(九)Redis实战(六)基于Redis队列实现异步秒杀下单

文章目录 前言4.5 分布式锁-Redisson4.5.4 Redission锁重试4.5.5 WatchDog机制4.5.5 MutiLock原理 4.6 秒杀优化4.6.1 优化方案4.6.2 完成秒杀优化 4.7 Redis消息队列4.7.1 基于List实现消息队列4.7.2 基于PubSub的消息队列4.7.3 基于Stream的消息队列4.7.4 基于Stream的消息队…

DIY可视化UniApp表格组件

表格组件在移动端的用处非常广泛,特别是在那些需要展示结构化数据、进行比较分析或提供详细信息的场景中。数据展示与整理:表格是展示结构化数据的理想方式,特别是在需要展示多列和多行数据时。通过表格,用户可以轻松浏览和理解数…

Spring Boot-02-依赖管理和自动配置

二、Spring Boot的两大重要机制 1. 依赖管理机制 开发什么场景,导入什么场景启动器,场景启动器自动把这个场景的所有核心依赖全部导入进来。maven依赖传递原则:A依赖B,B依赖C,则A就拥有B和C。每个boot项目都有一个父…

防火墙配置IPSec VPN【IPSecVPN概念及详细讲解】

防火墙配置IPSecVPN-点到点 配置目标 总公司内网与分公司内网互通 拓扑 配置ISP路由器 <Huawei>u t m <Huawei>sys [Huawei]sys ISP [ISP]interface g0/0/0 [ISP-GigabitEthernet0/0/0]ip address 100.1.1.102 24 [ISP-GigabitEthernet0/0/0]q [ISP]interface g…

【cocos creator】【编辑器插件】cocos creator文件复制时,解决cocos creator uuid冲突

&#xff01;&#xff01;&#xff01;修改前先备份 1、将文件夹放在packages文件夹下 2、打开项目&#xff0c;选择要刷新uuid的文件夹 3、菜单栏点击 扩展->refresh-uuid 4、等控制台提示&#xff1a;资源uuid刷新完成&#xff0c;重启项目&#xff08;&#xff01;&#…

VSCODE自动更新无法连接远程服务器报错“waiting for server log...“的解决方法

问题描述 一觉醒来打开vscode发现连接远程服务器显示无法连接&#xff0c;终端一直报错“waiting for server log…"&#xff0c;经查是因为vscode自动更新到了1.86&#xff0c;对于远程服务器的linux版本要求较高。这里记录下解决方法。 解决方法 1. 下载vscode便携版…

网络安全之命令注入

漏洞原理&#xff1a; 应用系统设计需要给用户提供指定的远程命令操作的接口&#xff0c;比如&#xff1a;路由器&#xff0c;防火墙&#xff0c;入侵检测等设备的web管理界面。一般会给用户提供一个ping操作的web界面 用户从web界面输入目标IP&#xff0c;提交后台会对改IP地…

Web 前端性能优化之五:构建优化

4、构建优化 资源的合并与压缩所涉及的优化点包括两方面&#xff1a;一方面是减少HTTP的请求数量&#xff0c;另一方面是减少HTTP请求资源的大小。 1、HTML 压缩 1、什么是 HTML 压缩 百度首页部分 HTML 源代码 谷歌首页部分 HTML 源代码 虽然这些格式化的字符能带来很好的代…

LiDAR点云转3D Tiles

到 2026年&#xff0c;一个国家项目旨在绘制整个法国领土的三维地图。 美国国家地理和林业信息研究所 (IGN) 是这个名为“Lidar HD”的“项目”的幕后黑手&#xff0c;其目标是获得该地区非常精确的 3D 描述。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生…

内外网数据交换发展进程:安全与便捷并行

随着信息化的不断推进&#xff0c;医院、党政以及企业的内外网数据交换正成为日益关注的焦点。在保障数据安全的前提下&#xff0c;需要寻求一种既安全可靠又操作便捷的数据传输方式。本文将探讨内外网数据交换发展进程&#xff0c;分析各种传输方式的优缺点&#xff0c;以及它…