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的扩展模式。
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云服务器可以干嘛 阿里云服务器应用场景有哪些

阿里云服务器可以干嘛?能干啥你还不知道么!简单来讲可用来搭建网站、个人博客、企业官网、论坛、电子商务、AI、LLM大语言模型、测试环境等,阿里云百科aliyunbaike.com整理阿里云服务器的用途: 阿里云服务器活动 aliyunbaike.com…

单例模式--理解

单例模式 单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。 单…

使用docker-compose创建多项目容器运行

使用docker-compose创建多项目容器运行 按招网友提供方法创建 docker-compose.yml内容(这里改了桥接模式,并且注释放开) version: "3" services:docker_python:image: python:2.7.18container_name: py_appworking_dir: "/r…

婴儿洗衣机哪种比较实用?精选四大热门口碑婴儿洗衣机推荐

对于有了宝宝的家庭来说,洗衣成为了一项重要的家务事。大家都知道,宝宝的皮肤比较娇嫩,容易受到各种细菌、病毒的侵扰。所以,宝宝的衣物应该与大人的分开洗。婴儿洗衣机作为一种专门为婴幼儿家庭设计的洗衣机,其具有除…

nginx部署前端教程

目录 一、前言二、部署三、注意四、参考 一、前言 一般来说现在的软件项目,都是分用户端以及管理端的,并且是前后端分离的,这里我来记录一下部署两个前端的教程。 部署前端之前需要的准备工作是部署springBoot后端程序,这里我do…

qt设置异形图片并实现透明效果

思路:将背景设置为透明,然后将图片设置给label,将laben和this都设置为图片大小 setAttribute(Qt::WA_TranslucentBackground, true); 可以将背景设置为透明 然后 QPixmap *pixnew QPixmap(":/Image/xxx.png"); this->setFixedSize(pix->width(),pix->…

对OceanBase中的配置项与系统变量,合法性检查实践

在“OceanBase 配置项&系统变量实现及应用详解”的系列文章中,我们已经对配置项和系统变量的源码进行了解析。当涉及到新增配置项或系统变量时,通常会为其指定一个明确的取值范围或定义一个专门的合法性检查函数。本文将详细阐述在不同情境下&#x…

深入理解指针2:数组名理解、一维数组传参本质、二级指针、指针数组和数组指针、函数中指针变量

目录 1、数组名理解 2、一维数组传参本质 3、二级指针 4、指针数组和数组指针 5、函数指针变量 1、数组名理解 首先来看一段代码: int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));return 0; } 输出的结果是&…

[大模型]大语言模型量化方法对比:GPTQ、GGUF、AWQ

在过去的一年里,大型语言模型(llm)有了飞速的发展,在本文中,我们将探讨几种(量化)的方式,除此以外,还会介绍分片及不同的保存和压缩策略。 说明:每次加载LLM示例后,建议清除缓存,以…

python之列表操作

1、创建列表 代码示例: i [1, 2, 34, 4] o list((1, 2, 3, 4, 5, 6)) 分别创建了两个数组,这两种格式都能创建数组 2、关于数组的操作 1、添加元素 1、append() append方法主要是添加一个元素 代码示例如下:…

深度学习理论基础(七)Transformer编码器和解码器

学习目录: 深度学习理论基础(一)Python及Torch基础篇 深度学习理论基础(二)深度神经网络DNN 深度学习理论基础(三)封装数据集及手写数字识别 深度学习理论基础(四)Parse…

手机软件何时统一--桥接模式

1.1 凭什么你的游戏我不能玩 2007年苹果手机尚未出世,机操作系统多种多样(黑莓、塞班、Tizen等),互相封闭。而如今,存世的手机操作系统只剩下苹果OS和安卓,鸿蒙正在稳步进场。 1.2 紧耦合的程序演化 手机…

vue的 blob文件下载文件时,后端自定义异常,并返回json错误提示信息,前端捕获信息并展示给用户

1.后端返回的json数据结构为: {"message":"下载失败,下载文件不存在,请联系管理员处理!","code":500} 2.vue 请求后台接口返回的 Blob数据 3.问题出现的原因是,正常其他数据列表接口&…

[C++][算法基础]堆排序(堆)

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。 输入格式 第一行包含整数 n 和 m。 第二行包含 n 个整数,表示整数数列。 输出格式 共一行,包含 m 个整数,表示整数数列中前 m 小的数。 数据范围 1≤m≤n≤&#x…

第4章 Redis,一站式高性能存储方案,笔记问题

点赞具体要实现功能有哪些? 可以点赞的地方:对帖子点赞,对评论点赞点一次是点赞,再点一次是取消赞统计点赞的数量(计数,string),帖子被点赞的数量,某个用户被点赞的数量…

【数据结构】考研真题攻克与重点知识点剖析 - 第 5 篇:树与二叉树

(考研真题待更新) 欢迎订阅专栏:408直通车 请注意,本文中的部分内容来自网络搜集和个人实践,如有任何错误,请随时向我们提出批评和指正。本文仅供学习和交流使用,不涉及任何商业目的。如果因本…

2024免费Mac电脑用户的系统清理和优化软件CleanMyMac

作为产品营销专家,对于各类产品的特性与优势有着深入的了解。CleanMyMac是一款针对Mac电脑用户的系统清理和优化软件,旨在帮助用户轻松管理、优化和保护Mac电脑。以下是关于CleanMyMac的详细介绍: CleanMyMac X2024全新版下载如下: https://…

阿里云乱扣费故障,技术堪忧

2024年4月3日,距离2023年11月的故障没有多久,阿里云又出现乱扣费故障,导致账号欠费3000多,oss,块存储,cdn等所有后付费服务停止工作,不知道这个故障能算什么级别的。 凌晨1点多,收到…

用Vue全家桶手工搓了一个类似抖音短视频的软件,全开源

用Vue全家桶手工搓了一个类似抖音短视频的软件,全开源 软件简介 用Vue全家桶手工搓了一个高仿抖音,全开源 PC浏览器请用手机模式访问。先按F12调出控制台,再按CtrlShiftM切换到手机模式,手机请用Via浏览器或者Chrome浏览器预览。…

Octopus V2:设备端super agent的高级语言模型

论文:Octopus v2: On-device language model for super agent论文地址:https://arxiv.org/abs/2404.01744模型主页:https://huggingface.co/NexaAIDev/Octopus-v2 Octopus-V2-2B Octopus-V2-2B 是一款具有20亿参数的开源先进语言模型&#…