C++筹划模式布局型模式———桥接模式

[复制链接]
发表于 2025-12-30 14:22:40 | 显示全部楼层 |阅读模式
一、弁言

桥接(Bridge)模式也叫桥梁模式,简称桥模式,是一种布局型模式。该模式所办理的标题非常简朴,即根据单一职责原则,在一个类中,不要做太多事,假如事变很多,只管拆分到多个类中去,然后在一个类中包罗指向别的一个类对象的指针,当必要实行别的一个类中的动作时,用指针直接去调用别的一个类的成员函数。

二、桥接模式

桥接模式是一种布局型筹划模式, 可将一个大类或一系列精密相干的类拆分为抽象和实现两个独立的条理布局, 从而能在开发时分别使用。
我们举个例子来阐明: 继续前面的闯关打架类游戏。在游戏中,不可制止地要表现各种图像,比方,人物头像、血条、人物背包、各种物品道具等,这些图像源自各种图像文件,从图像文件中把数据读出来并按照一个事先约定好的格式规范生存到一个缓冲区中以方便后续同一的表现处置处罚。
现在的标题是图像文件有多种格式,常用的包罗png、jpg、bmp等,为了把数据从这些差异格式的图像文件中读出(留意,不管文件是什么格式,读出到事先约定好的缓冲区中后都酿成遵照雷同规范的数据,此时这些数据不再有来自差异文件的区别)并表现,步调创建了一个叫作Image的父类以及分别叫作Image_png、Image_jpg、Image_bmp的子类,代码如下:
  1. class Image {
  2. public:
  3.     void draw(const string& pfilename) {
  4.         int iLen = 0;
  5.         string pData = parsefile(pfilename, iLen);
  6.         if (iLen > 0) {
  7.             cout << "显示pData所指向的缓冲区中的图像数据." << endl;
  8.         }
  9.     }
  10.     virtual ~Image() {}
  11. private:
  12.     //根据文件名分析文件内容,每个子类因为图像文件格式不同,会有不同的读取和处理代码
  13.     virtual string parsefile(const string& pfilename, int& iLen) = 0;
  14. };
  15. // 处理png格式的图像文件
  16. class Image_png : public Image {
  17. private:
  18.     virtual string parsefile(const string& pfilename, int& iLen) override {
  19.         cout << "开始分析png文件中的数据并将分析结果放到pData中,";
  20.         iLen = 100; // 模拟长度
  21.         string data(iLen, 'x'); // 使用字符串初始化模拟数据
  22.         return data;
  23.     }
  24. };
  25. // 处理jpg格式的图像文件
  26. class Image_jpg : public Image {
  27. private:
  28.     virtual string parsefile(const string& pfilename, int& iLen) override {
  29.         cout << "开始分析jpg文件中的数据并将分析结果放到pData中,";
  30.         iLen = 150; // 模拟长度
  31.         string data(iLen, 'y'); // 使用字符串初始化模拟数据
  32.         return data;
  33.     }
  34. };
  35. // 处理bmp格式的图像文件
  36. class Image_bmp : public Image {
  37. private:
  38.     virtual string parsefile(const string& pfilename, int& iLen) override {
  39.         cout << "开始分析bmp文件中的数据并将分析结果放到pData中,";
  40.         iLen = 200; // 模拟长度
  41.         string data(iLen, 'z'); // 使用字符串初始化模拟数据
  42.         return data;
  43.     }
  44. };
复制代码
为了扩大游戏的受众并增长营收,这款游戏必要支持多个使用体系,包罗 Windows、Linux 和 macOS。然而,这带来了一个标题:每个使用体系在表现图像数据时的实今世码都差异。固然 parsefile 成员函数的实现可以与使用体系无关,但 draw 成员函数中的表现代码则必要针对差异使用体系举行调解。
因此,步调员不得不为现有的 Image 类的子类(如 Image_png、Image_jpg 和 Image_bmp)创建额外的子类,以适配每个使用体系。这就意味着,假如我们为每种图像格式都增长三种使用体系的适配,本来的布局会变得非常复杂。
比方,本来有 3 个子类,现在必要为每个图片范例子类再增长 3 个使用体系的子类,统共会增长到 9 个新类,加上原有的 4 个类,统共是 13 个类。假如再支持一种新的图像格式,好比 GIF,就必要增长到 17 个类。而假如再增长一个新的使用体系,好比 Android,那么类的数目会增长到 21 个。
很显着,接纳继续布局来筹划类在这种环境下不是一个好方法。每当我们必要支持新的图像格式或新的使用体系时,类的数目就会敏捷增长,导致代码变得复杂且难以维护。
   桥接模式通过将继续改为组合的方式来办理这个标题。 详细来说, 就是抽取此中一个维度并使之成为独立的类条理, 如许就可以在初始类中引用这个新条理的对象, 从而使得一个类不必拥有全部的状态和活动。
  因此,我们不难发现,实在没有须要把图像文件格式和使用体系范例掺和到一起通过继续筹划出一系列类(比方Image_jpg_Linux这种类),如许筹划是违反单一职责原则的,可以像下面如许做:


  • 把图像文件格式单独筹划成一个继续关系的类,在此中实现parsefile成员函数(由于该成员函数只与图像文件格式有关)。
  • 使用体系范例也单独筹划成一个继续关系的类,在此中实现draw成员函数(由于该成员函数只与使用体系范例有关)。
如许无论是扩充图像文件格式还是使用体系范例这两组类中的哪一组,都不会影响别的一组类,也就不会造成子类数目标急速增长。固然,在图像文件格式体现的类中有一个指向使用体系范例体现的类对象的指针,从而构成这两个类之间的委托关系。下面给出改造后的代码:
  1. // 操作系统相关的接口
  2. class ImageOS {
  3. public:
  4.         virtual void draw(const string& data, int iLen) = 0;
  5.         virtual ~ImageOS() {}
  6. };
  7. // Windows 显示实现
  8. class ImageOS_Windows : public ImageOS {
  9. public:
  10.         void draw(const string& data, int iLen) override {
  11.                 cout << "在 Windows 上显示图像数据: " << data << endl;
  12.         }
  13. };
  14. // Linux 显示实现
  15. class ImageOS_Linux : public ImageOS {
  16. public:
  17.         void draw(const string& data, int iLen) override {
  18.                 cout << "在 Linux 上显示图像数据: " << data << endl;
  19.         }
  20. };
  21. // macOS 显示实现
  22. class ImageOS_Mac : public ImageOS {
  23. public:
  24.         void draw(const string& data, int iLen) override {
  25.                 cout << "在 macOS 上显示图像数据: " << data << endl;
  26.         }
  27. };
复制代码
紧接着,再给一个ImageFormat类,以及图像文件格式相干类。
  1. // 图像格式基类
  2. class ImageFormat {
  3. public:
  4.         ImageFormat(unique_ptr<ImageOS> pimgos) : m_pImgOS(move(pimgos)) {}
  5.         virtual void parsefile(const string& pfilename) = 0;
  6.         virtual ~ImageFormat() {}
  7. protected:
  8.         unique_ptr<ImageOS> m_pImgOS; // 委托
  9. };
  10. // 处理png格式的图像文件
  11. class Image_png : public ImageFormat {
  12. public:
  13.         Image_png(unique_ptr<ImageOS> pimgos) : ImageFormat(move(pimgos)) {}
  14.         void parsefile(const string& pfilename) override {
  15.                 cout << "开始分析 PNG 文件: " << pfilename << endl;
  16.                 //...
  17.         }
  18. };
  19. // 处理png格式的图像文件
  20. class Image_jpg : public ImageFormat {
  21. public:
  22.         Image_jpg(unique_ptr<ImageOS> pimgos) : ImageFormat(move(pimgos)) {}
  23.         void parsefile(const string& pfilename) override {
  24.                 cout << "开始分析 JPG 文件: " << pfilename << endl;
  25.                 //...
  26.         }
  27. };
  28. // 处理bmp格式的图像文件
  29. class Image_bmp : public ImageFormat {
  30. public:
  31.         Image_bmp(unique_ptr<ImageOS> pimgos) : ImageFormat(move(pimgos)) {}
  32.         void parsefile(const string& pfilename) override {
  33.                 cout << "开始分析 BMP 文件: " << pfilename << endl;
  34.                 //...
  35.         }
  36. };
复制代码
我们使用时:
  1. unique_ptr<ImageOS> windowsOS = make_unique<ImageOS_Windows>();
  2. unique_ptr<ImageFormat> pngImage = make_unique<Image_png>(move(windowsOS));
  3. pngImage->parsefile("image.png"); // 解析并显示图像数据
  4. unique_ptr<ImageOS> linuxOS = make_unique<ImageOS_Mac>();
  5. unique_ptr<ImageFormat> pngImageLinux = make_unique<Image_png>(move(linuxOS));
  6. pngImageLinux->parsefile("image.png"); // 解析并显示图像数据
复制代码
上述内容并不难懂确。

此时,假如增长一个对.gif文件格式的支持,必要增长一个Image_gif子类(以ImageFormat作父类),而不必要改动ImageOS和其于类,这紧张得益于ImageFormat子类中的parsefile成员函数得到的是一个事先约定好格规范的缓冲区数据,这些缓冲区数据已经脱离了原始的图像文件格式(png、jpg、bmp等)接纳了一种同一的格式来表达,以是在实行ImageFormat子类的parsefile成员函数时,所碰到的m_pImgOS->draw(presult,iLen)代码行会直接调用ImageOS子类的draw方员函数,在这个draw成员函数中,并不必要区分原始的图像数据来自何种格式的图像格式。
   桥接模式布局
  

引入桥接模式的界说:将抽象部门与实现部门分离,使它们都可以独立弟变革和扩展。


  • 抽象部门一样平常指业务功能,比方ImageFormat类,用于剖析各种差异的图像文件格式,这就归为业务功能
  • 实现部门一样平常指详细的平台实现,比方ImageOS类,用于根据差异的使用体系来绘制图像,这就归为平台实现。
也就是说,抽象部门是图像文件格式,而实现部门是OS的范例。桥接模式界说的意思就是把这两个维度分开,每个维度可以独立的变革。

三、总结


ImageFormat类和ImageOS之间的关系就叫桥接。用“桥接”筹划模式界说中用到的术语来说,桥接就在抽象部门与实现部门之间继承着桥梁作用,桥梁两侧的每一部门又都可以独立变革。
在桥接模式的UML图中,存在四种脚色:

  • 抽象部门接口Abstraction):这个脚色界说了一个抽象类的接口,并包罗一个指向Implementor范例对象的指针。ImageFormat类扮演了这个脚色。
  • 扩展抽象部门接口RefinedAbstraction):这个脚色实现了在Abstraction中界说的接口,而且可以调用Implementor中界说的方法。Image_png、Image_jpg、Image_bmp`这些类扮演了这个脚色。
  • 实现部门接口Implementor):这个脚色界说了实现类的接口,这些接口大概与Abstraction中的接口相似,也大概完全差异。通常Implementor提供的接口只包罗根本使用,而Abstraction中的接口则实现更复杂的功能。ImageOS类扮演了这个脚色。
  • 详细实现类ConcreteImplementor):这个脚色实现了Implementor中界说的接口。 ImageOS_Windows、ImageOS_Linux、ImageOS_Mac这些类扮演了这个脚色。
桥接模式用组合关系办理了传统继续关系存在的类数目爆炸式增长的标题,使用对象组合方式办理标题,使代码更机动、更易于扩展。桥接模式的实今世码不但体现了单一职责原则,还体现了开闭原则、组合复用原则、依靠倒置原则等。


  • 桥接模式通常会于开发前期举行筹划, 可以或许将步调的各个部门独立开来以便开发。 另一方面, 适配器模式通常在已有步调中使用, 让相互不兼容的类能很好地相助。
桥接、 状态模式和计谋模式 (在某种水平上包罗适配器) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不外也各自办理了差异的标题。 模式并不但是以特定方式构造代码的配方, 你还可以使用它们来和其他开发者讨论模式所办理的标题。
可以将抽象工厂模式和桥接搭配使用。 假如由桥接界说的抽象只能与特定实现相助, 这一模式搭配就非常有效。 在这种环境下, 抽象工厂可以对这些关系举行封装, 而且对客户端代码隐蔽其复杂性。也可以联合使用天生器模式和桥接模式: 主管类负责抽象工作, 各种差异的天生器负责实现工作。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表