设计模式8-桥模式

设计模式8-Bridge 桥模式

  • 由来与目的
  • 模式定义
  • 结构
  • 代码推导
      • 1. 类和接口的定义
      • 2. 平台实现
      • 3. 业务抽象
      • 4. 使用示例
      • 总结
      • 1. 类数量过多,复杂度高
      • 2. 代码重复
      • 3. 不符合单一职责原则
      • 4. 缺乏扩展性
      • 改进后的设计
        • 1. 抽象和实现分离(桥接模式)
        • 2. 抽象类
        • 3. 使用装饰者模式添加功能
        • 4. 使用示例
      • 改进后的优点
    • 疑问
      • 1. `protected` 访问级别的意义
      • 2. `protected` 的使用场景
      • 具体分析代码中的选择
        • 1. `protected` 访问级别的好处
        • 2. 如果使用`private`
        • 3. 如果使用`public`
      • 总结
  • 要点总结

桥模式也属于单一职责模式中的一种。

由来与目的

桥模式的由来以及目的:由于某些类型的固有的实现逻辑使他们具有两个变化的维度乃至多个维度的变化。那么此时如何应对这种多维度的变化,如何利用面向对象技术?来使得类型可以轻松地沿着两个乃至多个方向进行变化,而不引入额外的复杂度呢?那么乔模式就应运而生,他的存在就是为了解决此类问题。

模式定义

将抽象部分也就是业务功能,与实现部分分离,使他们都可以独立的变化。

结构

在这里插入图片描述

代码推导

class Messager{
public:virtual void Login(string username, string password)=0;virtual void SendMessage(string message)=0;virtual void SendPicture(Image image)=0;virtual void PlaySound()=0;virtual void DrawShape()=0;virtual void WriteText()=0;virtual void Connect()=0;virtual ~Messager(){}
};//平台实现class PCMessagerBase : public Messager{
public:virtual void PlaySound(){//**********}virtual void DrawShape(){//**********}virtual void WriteText(){//**********}virtual void Connect(){//**********}
};class MobileMessagerBase : public Messager{
public:virtual void PlaySound(){//==========}virtual void DrawShape(){//==========}virtual void WriteText(){//==========}virtual void Connect(){//==========}
};//业务抽象class PCMessagerLite : public PCMessagerBase {
public:virtual void Login(string username, string password){PCMessagerBase::Connect();//........}virtual void SendMessage(string message){PCMessagerBase::WriteText();//........}virtual void SendPicture(Image image){PCMessagerBase::DrawShape();//........}
};class PCMessagerPerfect : public PCMessagerBase {
public:virtual void Login(string username, string password){PCMessagerBase::PlaySound();//********PCMessagerBase::Connect();//........}virtual void SendMessage(string message){PCMessagerBase::PlaySound();//********PCMessagerBase::WriteText();//........}virtual void SendPicture(Image image){PCMessagerBase::PlaySound();//********PCMessagerBase::DrawShape();//........}
};class MobileMessagerLite : public MobileMessagerBase {
public:virtual void Login(string username, string password){MobileMessagerBase::Connect();//........}virtual void SendMessage(string message){MobileMessagerBase::WriteText();//........}virtual void SendPicture(Image image){MobileMessagerBase::DrawShape();//........}
};class MobileMessagerPerfect : public MobileMessagerBase {
public:virtual void Login(string username, string password){MobileMessagerBase::PlaySound();//********MobileMessagerBase::Connect();//........}virtual void SendMessage(string message){MobileMessagerBase::PlaySound();//********MobileMessagerBase::WriteText();//........}virtual void SendPicture(Image image){MobileMessagerBase::PlaySound();//********MobileMessagerBase::DrawShape();//........}
};void Process(){//编译时装配Messager *m =new MobileMessagerPerfect();
}

这段代码实现了一个跨平台的消息传递系统,其中包含了多个层次的抽象和具体实现。它通过将平台相关的功能和业务逻辑分离来实现不同的消息传递方式。

1. 类和接口的定义

class Messager {
public:virtual void Login(string username, string password) = 0;virtual void SendMessage(string message) = 0;virtual void SendPicture(Image image) = 0;virtual void PlaySound() = 0;virtual void DrawShape() = 0;virtual void WriteText() = 0;virtual void Connect() = 0;virtual ~Messager() {}
};

Messager 是一个抽象基类,定义了登录、发送消息、发送图片以及平台相关的功能(播放声音、绘制图形、写文本、连接)。

2. 平台实现

class PCMessagerBase : public Messager {
public:virtual void PlaySound() {//**********}virtual void DrawShape() {//**********}virtual void WriteText() {//**********}virtual void Connect() {//**********}
};class MobileMessagerBase : public Messager {
public:virtual void PlaySound() {//==========}virtual void DrawShape() {//==========}virtual void WriteText() {//==========}virtual void Connect() {//==========}
};

PCMessagerBaseMobileMessagerBase 是平台相关的具体实现类,分别实现了 PC 和移动平台上的功能。这两个类分别实现了播放声音、绘制图形、写文本和连接的方法。

3. 业务抽象

class PCMessagerLite : public PCMessagerBase {
public:virtual void Login(string username, string password) {PCMessagerBase::Connect();//........}virtual void SendMessage(string message) {PCMessagerBase::WriteText();//........}virtual void SendPicture(Image image) {PCMessagerBase::DrawShape();//........}
};class PCMessagerPerfect : public PCMessagerBase {
public:virtual void Login(string username, string password) {PCMessagerBase::PlaySound();//********PCMessagerBase::Connect();//........}virtual void SendMessage(string message) {PCMessagerBase::PlaySound();//********PCMessagerBase::WriteText();//........}virtual void SendPicture(Image image) {PCMessagerBase::PlaySound();//********PCMessagerBase::DrawShape();//........}
};class MobileMessagerLite : public MobileMessagerBase {
public:virtual void Login(string username, string password) {MobileMessagerBase::Connect();//........}virtual void SendMessage(string message) {MobileMessagerBase::WriteText();//........}virtual void SendPicture(Image image) {MobileMessagerBase::DrawShape();//........}
};class MobileMessagerPerfect : public MobileMessagerBase {
public:virtual void Login(string username, string password) {MobileMessagerBase::PlaySound();//********MobileMessagerBase::Connect();//........}virtual void SendMessage(string message) {MobileMessagerBase::PlaySound();//********MobileMessagerBase::WriteText();//........}virtual void SendPicture(Image image) {MobileMessagerBase::PlaySound();//********MobileMessagerBase::DrawShape();//........}
};

这些类继承自平台实现类,并且在每个方法中调用了相应的基类方法,添加了业务逻辑。具体而言,PCMessagerLiteMobileMessagerLite 是简化版的实现,而 PCMessagerPerfectMobileMessagerPerfect 则是带有更多功能(如播放声音)的完整实现。

4. 使用示例

void Process() {//编译时装配Messager *m = new MobileMessagerPerfect();
}

Process 函数展示了如何创建一个 MobileMessagerPerfect 实例并赋值给 Messager 指针。

总结

这段代码通过将不同平台的实现和业务逻辑分开,展示了如何使用继承和多态来实现跨平台的消息传递系统。它遵循了开闭原则(对扩展开放,对修改关闭),使得添加新的平台或功能变得更容易。具体地,Messager 定义了接口,PCMessagerBaseMobileMessagerBase 实现了平台相关的功能,而 PCMessagerLitePCMessagerPerfectMobileMessagerLiteMobileMessagerPerfect 实现了具体的业务逻辑。

这段代码虽然展示了一个灵活的跨平台消息传递系统的设计,但仍然存在一些缺陷和改进空间。以下是一些主要的缺陷及其解决方案:

1. 类数量过多,复杂度高

缺陷:每添加一个新功能或新平台,都需要创建大量新的类,导致类数量爆炸,假设此时平台类数量为n ,平台实现功能类数量为m ,那么此时类的数量为1+n+m*n,增加了代码的复杂性和维护成本。
解决方案:可以使用桥接模式(Bridge Pattern),将抽象和实现分离,从而减少类的数量和复杂度。

2. 代码重复

缺陷:许多方法在不同类中有重复的实现,如 PlaySound()DrawShape()WriteText()Connect() 方法在不同类中基本相同。
解决方案:将这些方法提取到一个单独的类中,或者使用组合而不是继承来减少代码重复。

3. 不符合单一职责原则

缺陷Messager 类同时处理平台相关的实现和业务逻辑,违背了单一职责原则,使得类的职责不清晰。
解决方案:将平台相关的实现和业务逻辑分离,使用桥接模式或策略模式将这些职责分开。

4. 缺乏扩展性

缺陷:添加新功能(如加密、压缩等)需要创建大量新的类,缺乏灵活性。
解决方案:可以使用装饰者模式(Decorator Pattern)来动态地为对象添加职责,而不需要创建大量的子类。

改进后的设计

使用桥接模式和装饰者模式重新设计:

1. 抽象和实现分离(桥接模式)
class MessagerImp {
public:virtual void PlaySound() = 0;virtual void DrawShape() = 0;virtual void WriteText() = 0;virtual void Connect() = 0;virtual ~MessagerImp() {}
};class PCMessagerImp : public MessagerImp {
public:void PlaySound() override {// PC平台实现}void DrawShape() override {// PC平台实现}void WriteText() override {// PC平台实现}void Connect() override {// PC平台实现}
};class MobileMessagerImp : public MessagerImp {
public:void PlaySound() override {// 移动平台实现}void DrawShape() override {// 移动平台实现}void WriteText() override {// 移动平台实现}void Connect() override {// 移动平台实现}
};
2. 抽象类
class Messager {
protected:MessagerImp* imp;
public:Messager(MessagerImp* imp) : imp(imp) {}virtual void Login(string username, string password) = 0;virtual void SendMessage(string message) = 0;virtual void SendPicture(Image image) = 0;virtual ~Messager() {}
};class MessagerLite : public Messager {
public:MessagerLite(MessagerImp* imp) : Messager(imp) {}void Login(string username, string password) override {imp->Connect();// Lite版特有的登录逻辑}void SendMessage(string message) override {imp->WriteText();// Lite版特有的发送消息逻辑}void SendPicture(Image image) override {imp->DrawShape();// Lite版特有的发送图片逻辑}
};class MessagerPerfect : public Messager {
public:MessagerPerfect(MessagerImp* imp) : Messager(imp) {}void Login(string username, string password) override {imp->PlaySound();imp->Connect();// Perfect版特有的登录逻辑}void SendMessage(string message) override {imp->PlaySound();imp->WriteText();// Perfect版特有的发送消息逻辑}void SendPicture(Image image) override {imp->PlaySound();imp->DrawShape();// Perfect版特有的发送图片逻辑}
};
3. 使用装饰者模式添加功能
class MessagerDecorator : public Messager {
protected:Messager* messager;
public:MessagerDecorator(Messager* messager) : Messager(messager->imp), messager(messager) {}void Login(string username, string password) override {messager->Login(username, password);}void SendMessage(string message) override {messager->SendMessage(message);}void SendPicture(Image image) override {messager->SendPicture(image);}
};class EncryptedMessager : public MessagerDecorator {
public:EncryptedMessager(Messager* messager) : MessagerDecorator(messager) {}void Login(string username, string password) override {// 加密登录MessagerDecorator::Login(username, password);}void SendMessage(string message) override {// 加密消息MessagerDecorator::SendMessage(message);}void SendPicture(Image image) override {// 加密图片MessagerDecorator::SendPicture(image);}
};
4. 使用示例
void Process() {MessagerImp* pcImp = new PCMessagerImp();Messager* m = new EncryptedMessager(new MessagerPerfect(pcImp));m->Login("user", "password");m->SendMessage("Hello, World!");// Clean updelete m;delete pcImp;
}

改进后的优点

  1. 减少类数量:通过桥接模式,将平台相关实现和业务逻辑分离,减少了类的数量,此时类的数量为(1+n+m) < (1+n+m*n)。
  2. 减少代码重复:将公共方法提取到实现类中,减少了代码重复。
  3. 职责清晰:将平台相关实现和业务逻辑分离,符合单一职责原则。
  4. 增加扩展性:通过装饰者模式,能够动态地添加新功能,而无需创建大量新的子类。

通过这些改进,可以显著提高代码的可维护性、可扩展性和清晰性。

疑问

在面向对象编程中,选择成员变量的访问级别时,需要考虑类的设计和使用场景。在这段代码中,Messager类的imp成员变量被声明为protected,而不是private或者public,这是有特定原因的。

1. protected 访问级别的意义

protected 访问级别允许子类访问和修改基类中的成员变量,同时对类外部保持不可见。这样可以在保证类的封装性的同时,允许子类进行适当的扩展和修改。

2. protected 的使用场景

在这段代码中,Messager类的设计是为了作为一个基类,它的主要作用是定义接口和提供基础的实现,而具体的实现细节由其子类来完成。因此,将imp成员变量设为protected可以让子类方便地访问和操作这个变量,而无需通过公共接口来访问。

具体分析代码中的选择

1. protected 访问级别的好处
class Messager {
protected:MessagerImp* imp;
public:Messager(MessagerImp* imp) : imp(imp) {}virtual void Login(string username, string password) = 0;virtual void SendMessage(string message) = 0;virtual void SendPicture(Image image) = 0;virtual ~Messager() {}
};class MessagerLite : public Messager {
public:MessagerLite(MessagerImp* imp) : Messager(imp) {}void Login(string username, string password) override {imp->Connect();// Lite版特有的登录逻辑}void SendMessage(string message) override {imp->WriteText();// Lite版特有的发送消息逻辑}void SendPicture(Image image) override {imp->DrawShape();// Lite版特有的发送图片逻辑}
};

在这种设计下,MessagerLite和其他子类可以直接访问imp,这使得子类可以轻松地调用平台相关的实现方法,如Connect()WriteText()DrawShape()

2. 如果使用private

如果将imp设为private,子类将无法直接访问imp,需要通过公共的getter和setter方法。这增加了额外的复杂性,而且在某些情况下可能会影响性能和代码的可读性。

class Messager {
private:MessagerImp* imp;
public:Messager(MessagerImp* imp) : imp(imp) {}MessagerImp* getImp() { return imp; }virtual void Login(string username, string password) = 0;virtual void SendMessage(string message) = 0;virtual void SendPicture(Image image) = 0;virtual ~Messager() {}
};class MessagerLite : public Messager {
public:MessagerLite(MessagerImp* imp) : Messager(imp) {}void Login(string username, string password) override {getImp()->Connect();// Lite版特有的登录逻辑}void SendMessage(string message) override {getImp()->WriteText();// Lite版特有的发送消息逻辑}void SendPicture(Image image) override {getImp()->DrawShape();// Lite版特有的发送图片逻辑}
};
3. 如果使用public

如果将imp设为public,则任何外部代码都可以直接访问和修改imp,这违反了封装原则,容易导致错误和不必要的依赖。

class Messager {
public:MessagerImp* imp;Messager(MessagerImp* imp) : imp(imp) {}virtual void Login(string username, string password) = 0;virtual void SendMessage(string message) = 0;virtual void SendPicture(Image image) = 0;virtual ~Messager() {}
};// 外部代码可以直接访问 imp
Messager* m = new MessagerLite(new PCMessagerImp());
m->imp->Connect(); // 不推荐

总结

MessagerImp* imp设为protected是为了在保持封装性的同时,让子类能够方便地访问和使用该成员变量。这种设计既保证了基类和子类之间的良好协作,又避免了将实现细节暴露给类的外部,提高了代码的可维护性和扩展性。

要点总结

  • 模式使用对象间的组合关系解耦合了。抽象和现实之间固有的绑定关系,使得抽象可实现可以沿着各自的维度来变化所,所谓抽象和实现沿着各自维度的变化,即子类化他们
  • 强模式有时候类似于多继承方案。但是多继承方案往往违背单一职责,原则即一个类只有一个变化的原因,复用性比较差。桥模式是比多继承方案更好的解决方法。
  • 乔模桥模式的应用一般在两个非常强的变化维度,有时一个内页。有。多于两个的变化维度,这时可以使用桥的扩展模式。

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

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

相关文章

学习XDMA—20240709

概览&#xff1a; 在内部&#xff0c;子系统可以配置为实现多达8个独立的物理DMA引擎(最多4个H2C和4个C2H)。这些DMA引擎可以映射到单独的AXI4Stream接口&#xff0c;也可以将共享的AXI4内存映射(MM)接口映射到用户应用程序。在axis4 MM接口上&#xff0c;PCI Express的DMA/桥接…

linux查看目录下的文件夹命令,find 查找某个目录,但是不包括这个目录本身?

linux查看目录下的文件夹命令&#xff0c;find 查找某个目录&#xff0c;但是不包括这个目录本身&#xff1f; Linux中查看目录下的文件夹的命令是使用ls命令。ls命令用于列出指定目录中的文件和文件夹。通过不同的选项可以实现显示详细信息、按照不同的排序方式以及使用不同的…

Profibus转ModbusTCP网关模块实现Profibus_DP向ModbusTCP转换

Profibus和ModbusTCP是工业控制自动化常用的二种通信协议。Profibus是一种串口通信协议&#xff0c;它提供了迅速靠谱的数据传输和各种拓扑结构&#xff0c;如总线和星型构造。Profibus可以和感应器、执行器、PLC等各类设备进行通信。 ModbusTCP是一种基于TCP/IP协议的通信协议…

一次零基础 自“信息收集“到“权限维持“的渗透测试全程详细记录

一、渗透总流程 1.确定目标&#xff1a; 在本靶场中&#xff0c;确定目标就是使用各种扫描工具进行ip扫描&#xff0c;确定目标ip。 2.信息收集&#xff1a; 比如平常挖洞使用fofa&#xff0c;天眼查&#xff0c;ip域名等进行查&#xff0c;在我们这个靶场中比如使用Wappalyz…

基于网络编码的 tcp 变种-tcp/nc

tcp/nc 是指 “tcp with network coding”&#xff0c;是一种结合了网络编码技术的 tcp 变种&#xff0c;网上资源很少&#xff0c;我也不准备多介绍&#xff0c;只介绍它的核心。 传统 tcp 在演进过程中一直搞不定效率问题&#xff0c;网络带宽在增长&#xff0c;cpu 却没有变…

区间贪心

目录 1.贪心算法的思想 2.区间贪心算法常用的一些题目类型 1.选择最多不相交区间问题 P2970 [USACO09DEC] Selfish Grazing S 1.思路分析 2.上代码 2.区间选点问题 P1250 种树 1.题目 2.方法一 1.代码解释 3.方法二 3.区间合并问题 P2434 [SDOI2005] 区间 1. 思路…

Java集合面试题

Java集合框架 1、List、Set、Map的区别2、ArrayList、LinkedList、Vector区别3、为什么数组索引从0开始&#xff0c;而不是从1开始&#xff1f;4、ArrayList底层的实现原理5、红黑树、散列表6、HashMap的底层原理7、HashMap的put方法具体流程8、HashMap的扩容机制9、HashMap是怎…

南方科技大学马永胜教授给年轻人使用AI工具上的建议

摘要 - 1. AI的未来&#xff0c;是机器人和机器人之间的合作&#xff1b; 2. 行业的发展方向是需求决定的&#xff0c;不要做同质化的发展&#xff0c;要做专/精/特/新&#xff1b; 3. 新质生产力 &#xff08; 科学技术革命性突破 生产要素创新型配置 产业深度转型升级&…

java通过poi-tl导出word实战详细步骤

文章目录 与其他模版引擎对比1.引入maven依赖包2.新建Word文档exportWprd.docx模版3.编写导出word接口代码4.导出成果 poi-tl是一个基于Apache POI的Word模板引擎&#xff0c;也是一个免费开源的Java类库&#xff0c;你可以非常方便的加入到你的项目中&#xff0c;并且拥有着让…

Pix4Dmapper:无人机测绘的革命性工具

在现代测绘和地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;Pix4Dmapper无疑是一款革命性的工具。作为一名长期使用这款软件的用户&#xff0c;我深深感受到它在工作中的重要性和便利性。Pix4Dmapper不仅仅是一款软件&#xff0c;更是测绘工作者的得力助手&#xff…

285个地级市出口产品质量及技术复杂度(2011-2021年)

出口产品质量与技术复杂度&#xff1a;衡量国家竞争力的关键指标 出口产品质量是衡量国内企业生产的产品在国际市场上竞争力的重要标准。它不仅要求产品符合国际标准和目标市场的法律法规&#xff0c;而且需要保证产品质量的稳定性和可靠性。而出口技术复杂度则进一步体现了一…

批量下载手机中APP程序中文件

需求 利用 adb pull 下载手机中app的某目录 adb pull 命令本身不支持直接下载整个目录&#xff08;文件夹&#xff09;及其所有子目录和文件作为一个单一的操作。但是&#xff0c;可以通过一些方法来间接实现这一目的。 方法 1. 首先将要下载的目录进行 tar 打包 # 在 And…

【atcoder】习题——位元枚举

题意&#xff1a;求i&M的popcount的和&#xff0c;i属于0……N 主要思路还是变加为乘。 举个例子N22&#xff0c;即10110 假设M的第3位是1&#xff0c;分析N中&#xff1a; 00110 00111 00100 00101 发现其实等价于 0010 0011 0000 0001 也就是左边第4位和第5…

算法学习笔记(8.1)-动态规划入门

目录 问题特性&#xff1a; 最优子结构&#xff1a; 代码示例&#xff1a;&#xff08;动态规划最优子结构&#xff09; 上述最小代价爬楼梯的运行过程&#xff1a; 代码示例&#xff1a; 无后效性&#xff1a; 解析&#xff1a; 具体过程图示如下&#xff1a; 具体的…

如何为IP申请SSL证书

目录 以下是如何轻松为IP地址申请SSL证书的详细步骤&#xff1a; 申请IP证书的基本条件&#xff1a; 申请IP SSL证书的方式&#xff1a; 确保网络通信安全的核心要素之一&#xff0c;是有效利用SSL证书来加密数据传输&#xff0c;特别是对于那些直接通过IP地址访问的资源。I…

使用 Azure DevOps Pipelines 生成 .NET Core WebJob 控制台应用 CI/CD

Web 应用程序通常需要作为后台任务运行的进程&#xff0c;并在特定时间间隔进行计划或在事件中触发。它们不需要花哨的 IO 接口&#xff0c;因为重点是过程而不是输出。Azure WebJobs 提供了出色的支持&#xff0c;通常在云环境中通过 Web 控制台应用程序来实现此目的。WebJob …

Mac平台虚拟机 Parallels Desktop v19.4.1,支持M1/M2/M3芯片组

Parallels Desktop for Mac是功能强大灵活度高的虚拟化方案&#xff0c;无需重启即可在同一台电脑上随时访问Windows和Mac两个系统上的众多应用程序。从仅限于PC的游戏到生产力软件&#xff0c;Parallels Desktop都能帮您实现便捷使用。Parallels Desktop 是一款专业的Mac虚拟机…

Docker搭建kafka+zookeeper以及Springboot集成kafka快速入门

参考文章 【Docker安装部署KafkaZookeeper详细教程】_linux arm docker安装kafka-CSDN博客 Docker搭建kafkazookeeper 打开我们的docker的镜像源配置 vim /etc/docker/daemon.json 配置 { "registry-mirrors": ["https://widlhm9p.mirror.aliyuncs.com"…

Linux Mac 安装Higress 平替 Spring Cloud Gateway

Linux Mac 安装Higress 平替 Spring Cloud Gateway Higress是什么?传统网关分类Higress定位下载安装包执行安装命令执行脚本 安装成功打开管理界面使用方法configure.shreset.shstartup.shshutdown.shstatus.shlogs.sh Higress官网 Higress是什么? Higress是基于阿里内部的…

思考:Java内存模型和硬件内存模型

前言 前一阵在看volatile的原理&#xff0c;看到内存屏障和缓存一致性&#xff0c;发现再往底层挖就挖到了硬件和Java内存模型。这一块是自己似懂非懂的知识区&#xff0c;我一般称之为知识混沌区。因此整理这一篇文章。 什么是内存模型&#xff08;Memory Model&#xff09;…