当前位置: 首页 > news >正文

C++设计模式_结构型模式_桥接模式Bridge

桥接(Bridge)模式也叫桥梁模,简称桥模式,是一种结构型模式。
依赖倒置原则:高层组件和底层组件之间,通过一个类进行解耦;
单一职责原则:一个类中,不要做太多事情,如果事情太多就拆分到其他类中。然后在一个类中包含指向另处一个类对象的指针,当需要执行另外一个类中的动作时,用指针直接去调用另外一个类的成员函数。(这使用聚合方式实现)

一个传统的继承范例导致子类数量爆炸式增长

使用继承方式实现图片显示的例子。游戏中的绘图,比如人物头像,血条,背包,各种物品道具等,需要将图片读到内存,然后按照一定事先约定好的格式规范保存到一个缓冲区中以方便后续统一的显示处理,代码如下:

	class Image{public:// 根据缓冲区的内容以及缓冲区的长度,将这些数据显示出来void draw(const char* pfilename){int iLen = 0;char* pData = parsefile(pfilename, iLen);if (iLen > 0){cout << "显示 pData 所指向的缓冲区中的图像数据" << endl;delete pData;}}virtual ~Image() {} //做父类时析构函数应该为虚函数private:// 根据文件名分析文件内容,每个子类中的处理代码不同virtual char* parsefile(const char* pfilename, int& iLen) = 0;};// 处理png 格式图像文件的显示class Image_png :public Image{private:virtual char* parsefile(const char* pfilename, int& iLen){cout << "从图像读取数据到缓冲区中" << endl;iLen = 100;char* presult = new char[iLen];// 。。。return presult;}};// 处理jpg 格式图像文件的显示class Image_jpg :public Image{private:virtual char* parsefile(const char* pfilename, int& iLen){cout << "读取 jpg 格式图像,将数据放入缓冲区中" << endl;iLen = 200;char* presult = new char[iLen];// 。。。return presult;}};// 处理bmp 格式图像文件的显示class Image_bmp :public Image{private:virtual char* parsefile(const char* pfilename, int& iLen){cout << "读取 bmp 格式图像,将数据放入缓冲区中" << endl;iLen = 300;char* presult = new char[iLen];// 。。。return presult;}};void test(){Image* pImage = new Image_png;pImage->draw("a.png");  // 相当于调用了Image_png::draw("a.png"),draw函数中调用了Image_png 的parsefile函数Image* pImage2 = new Image_jpg;pImage2->draw("b.jpg");Image* pImage3 = new Image_bmp;pImage3->draw("c.bmp");}

上边代码的层次结构如下:
在这里插入图片描述
为了扩大游戏的受众面,进一步增加营收,公司决定,这个闯关打斗类游戏不但要能在Windows操作系统平台上运行,也要能够在 Linux以及Macos平台上运行。每种不同的操作系统,Windows、Linux、Mac示缓冲区中图像数据的实现代码都不相同,也就是说,parsefile成员函数的实现代码与操作系统无关,而 draw 成员函数中用于显示 pData 所指向的缓冲区中的图像数据的代码却与操作系统相关。程序员不得不扩展现有的Image类的子类(Image_png、Image_jpg、Image_bmp), 进一步为每个子类继续产生针对不同操作系统平台的3个子类,此时,类的继承层次图变成如下图所示:
在这里插入图片描述
与图16.1比较,不难发现,采用继承结构来设计类时,图16.2整整多出了9个新类,达到了13(1+3+3X3)个类之多。如果要多支持一个新的图像文件格式,例如gif 文件格式则会变成17(1+4+4X3)个类,若在17个类的基础上再多支持一个新的操作系统例如手上的安卓操作系统,则要达到21(1+4+4 X 4)个类。显然,采用继承结构来设计类不是一种好方法,会导致类的极速增长。

将类与类之间的继承关系改为委托关系

类之间一般为继承和组合关系,组合关系细分出一种叫做委托关系。将上面的代码中类的继承改为类的委托,则可以避免子类数量的快速增长。下面使用委托来改造上面的例子。
在上述范例中,所支持的图像文件格式包括3种–png、jpg、bmp, 所支持的操作系统类型也有3种—Windows,Linux、MacOS。有两点需要说明:
**说明1:**parsefile 成员函数用于从各种格式的图像文件中读取数据并放入一个缓冲区中,这个成员函数与操作系统类型无关,只是根据路径读取到数据,并放入新开辟的缓冲区中。
**说明2:**draw 成员函数用于显示parsefile 成员函数所返回的缓冲区图像,成员函数与操作系统有关,不同的操作系统draw时实现代码不同。
**改造方式:**把图像文件格式单独设计成一个继承关系的类,在其中实现ParseFile成员函数,因为parsefile 成员函数只读取数据,与具体的平台无关;把操作系统类型设计为一个基类,在类中实现draw成员函数,因为不同的操作系统draw不同。

    // 操作系统类型相关类class ImageOS{public:virtual void draw(const char* filename, int& iLen) = 0;virtual ~ImageOS(){}// 左父类时,解析函数应该为虚函数};class ImageOS_Windows : public ImageOS{public:virtual void draw(const char* filename, int& iLen) override{cout << "在ImageOS_Windows操作系统上显示pData所指向的缓冲区中图像数据" << endl;// 具体处理代码}};class ImageOS_Linux : public ImageOS{public:virtual void draw(const char* filename, int& iLen) override{cout << "在ImageOS_Linux操作系统上显示pData所指向的缓冲区中图像数据" << endl;// 具体处理代码}};class ImageOS_MACOS : public ImageOS{public:virtual void draw(const char* filename, int& iLen) override{cout << "在ImageOS_MACOS操作系统上显示pData所指向的缓冲区中图像数据" << endl;// 具体处理代码}};//  图像文件格式相关类class ImageFormat{public:ImageFormat(ImageOS* pimgos): m_pImgOS(pimgos){}// 根据文件名分析文件内容,每个子类因为图像文件格式不同,会有不同的读取和处理代码virtual void parseFile(const char* pfileName) = 0;virtual ~ImageFormat(){}protected:ImageOS* m_pImgOS; // 委托};class Image_png : public ImageFormat{public:Image_png(ImageOS* imageos): ImageFormat(imageos){}// 开始分析 png文件中的数据并 将分析结果放在padata中virtual void parseFile(const char* filename){int iLen = 100;char* result = new char[100];m_pImgOS->draw(result, iLen);delete result;}};class Image_jpg : public ImageFormat{public:Image_jpg(ImageOS* imageos): ImageFormat(imageos){}// 开始分析 png文件中的数据并 将分析结果放在padata中virtual void parseFile(const char* filename){int iLen = 100;char* result = new char[100];m_pImgOS->draw(result, iLen);delete result;}};class Image_bmp : public ImageFormat{public:Image_bmp(ImageOS* imageos): ImageFormat(imageos){}// 开始分析 png文件中的数据并 将分析结果放在padata中virtual void parseFile(const char* filename){int iLen = 100;char* result = new char[100];m_pImgOS->draw(result, iLen);delete result;}};void test(){ImageOS* pOS = new ImageOS_MACOS(); // 创建macos ImageFormat* pFormat = new Image_png(pOS);pFormat->parseFile("hello");// 显示MacOS上的png }

引入桥接模式

上面改造后的代码的UML图如下图所示:
在这里插入图片描述
对比一下继承实现方式和组合实现方式,可以看出,如果增加一个显示gif图片格式,继承关系下需要增加4个类;而组合关系下只需要增加一个类即可。
这个例子中存在着两个独立变化维度:第一个独立变化维度是图像文件格式,例如,可以有 png、jpg、bmp 等;第二个独立变化维度是所支持的操作系统类型,例如,可以有 Windows、Linux、MacOS,如下图所示:
在这里插入图片描述
引人“桥接”设计模式的定义: 将抽象部分与其实现部分分离,使它们都可以独立地变化和扩展。
抽象部分一般指业务功能, 例如ImageFormat类,用于解析各种不同的图像文件格式,这就归为业务功能。
实现部分一般指具体的平台实现,例如ImageOS类,用于根据不同的操作系统来绘制图像,这就归为平台实现。
桥接模式中桥接的得名,源自于UML图的ImageFormat 和 ImageOS类之间的连线(这两个类是聚合关系),这个连线像一座连接两个独立继承结构(指[mageFormat 和ImageOS)的桥,而诸多子类,例如Image_png、Imagejpg、Image_bmp以及ImageOS_Windows、ImageOS_Linux、ImageOs Mac 可以看成是诸多桥墩(支起了这座桥),如图16.5所示。
在这里插入图片描述
桥接模式的UML图中包含4种角色。
(1) Abstraction(抽象部分相关接口):定义抽象类的接口,其中包含一根指向Implementor类型对象的指针。这里指ImageFormat类。
(2) RefinedAbstraction(扩充抽象部分接口):实现在Abstraction中定义的接口,还可以在其中调用在【mplementor 中定义的方法。这里指Image_png、Image_jpg、lmage
(3) Implementor(实现部分相关接口):定义实现类的接口,这些接口可能与bmp类。Abstraction中的接口一致,也可能完全不同。通常Implementor 中定义的接口仅提供基本操作,而 Abstraction中定义的接口会实现更多、更复杂的功能。这里指ImageOS类。(4) Concretelmplementor(实现部分具体类):实现在Implementor 中定义的接口。这里指 ImageOS Windows、ImageOS_Linux,ImageOS_Mac 类。
桥接模式用组合关系解决了传统继承关系存在的类数量爆炸式增长的问题,使用对象组合方式解决问题,使代码更灵活、更易于扩展。桥接模式的实现代码不仅体现了单一职责原则,还体现了开闭原则、组合复用原则、依赖倒置原则等。

http://www.dtcms.com/a/469661.html

相关文章:

  • 关于flutter插件的存储位置问题
  • 把“Mixed Content”吃干抹净——一次 https→http 踩坑实录
  • 中山大学联合项目 论文解读 | iManip:面向机器人操作的技能增量学习
  • Unity:Json笔记——Json文件格式、JsonUtlity序列化和反序列化
  • 第八章 惊喜15 小萍收获初会
  • RabbitMQ基础知识与Spring Boot 3.x集成案例
  • 租房网站建设多少钱网站域名怎么改
  • Redis CPU高负载案例分析
  • ARMv9 CCA机密计算架构演进技术解析:重塑云原生时代的数据安全基石
  • 湖州网站设计浙北数据最新发布的手机有哪些
  • AD加域账号权限设置
  • 解决idea报错:Error running TrustApexCrmApplication. Command line is too long
  • 网站开发淄博进口商品代理平台
  • systme V共享内存(version1)
  • 万网网站制作wordpress投稿管理系统
  • python(47) : 快速截图[Windows工具(2)]
  • VSCODE GDB调试
  • 江西企业网站定制wordpress网页效果
  • CCF-GESP 等级考试 2024年6月认证C++三级真题解析
  • 前端学习1(学习时间:30分钟简单)
  • vlan范围
  • 北京顺义做网站编程正规学校有哪几所
  • 跨平台向量库:Linux Windows 上一条龙部署 PostgreSQL 向量扩展
  • 基于YOLO与DeepSort的高效行人跟踪算法研究
  • [GazeTracking] 视线追踪核心逻辑 | Dlib库
  • docker安装Xinference
  • 从标准化到个性化:基于代理模式的智能办公工具技术实现与趋势
  • AI(学习笔记第十一课) 使用langchain的multimodality
  • 如何配置iis网站国企500强完整名单
  • ppt做的最好的网站有哪些品牌建设方案和思路