设计模式(七)-----桥接模式(Bridge Pattern)

news2024/10/7 20:24:55

目录

  • 什么是桥接模式
    • 优点
    • 缺点
    • 应用场景
  • 基本结构
  • 业务场景
    • 不使用模式的解决方案
      • 实现发送普通消息
      • 实现发送加急消息
      • 实现发送特急消息
      • 添加发送手机消息的处理方式
    • 使用桥梁模式来解决问题

什么是桥接模式

将抽象部分与他的实现部分分离,这样抽象化与实现化解耦,使他们可以独立的变化.如何实现解耦的呢,就是通过提供抽象化和实现化之间的桥接结构

在现实生活中也有很多这样的例子,一个物品在搭配不同的配件时会产生不同的动作和结果,例如一辆赛车搭配的是硬胎或者是软胎就能够在干燥的马路上行驶,而如果要在下雨的路面行驶,就需要搭配雨胎了,这种根据行驶的路面不同,需要搭配不同的轮胎的变化的情况,我们从软件设计的角度来分析,就是一个系统由于自身的逻辑,会有两个或多个维度的变化,有时还会形成一种树状的关系,而为了应对这种变化,我们就可以使用桥接模式来进行系统的解耦

桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口对象来调用具体实现部分的功能。也就是说,桥接模式中的桥接是一个单方向的关系,只能够抽象部分去使用实现部分的对象,而不能反过来。

桥接模式符合“开闭原则”,提高了系统的可拓展性,在两个变化维度中任意扩展一个维度,都不需要修改原来的系统;并且实现细节对客户不透明,可以隐藏实现细节。但是由于聚合关系建立在抽象层,要求开发者针对抽象进行编程,这增加系统的理解和设计难度。

  • 抽象化:将复杂物体的一个或几个共同的特性抽出去而只注意其他特性的行动或过程。在java面向对象中抽象化就是将对象的共同性质抽取出去形成类的过程。
  • 实现化:针对抽象化给出的具体实现,它和抽象化是一个互逆的过程,实现化是对抽象化事物的进一步具体化
  • 解耦:解耦是将抽象化和实现化之间的耦合关系解脱开,或者说是将他们之间的强关联改换成弱关联。将两个角色之间的继承关系修改为关联关系

优点

  • 分离抽象接口及其实现部分。提高了比继承更好的解决方案.
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原来的系统.
  • 实现细节对客户不透明,可以隐藏实现细节.

缺点

  • 桥接模式的引入会增加系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行编程.

应用场景

  • 系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,则可以通过桥接模式使他们在抽象层建立一个关联关系;
  • 系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
  • 一个类存在两个独立变化的维度,而这两个维度都需要进行扩展。

基本结构

下图所示就是一个实现了桥梁模式的示意性系统的结构图:
在这里插入图片描述
该系统有两个等级结构

  • 由抽象化角色和修正抽象化角色组成的抽象化等级结构。
  • 由实现化角色和两个具体实现化角色所组成的实现化等级结构。

桥梁模式涉及的角色有:

  • 抽象化角色(Abstraction):抽象化给出的定义,并保存一个对实现化对象的引用。
  • 修正抽象化角色(RefineAbstraction):拓展抽象化角色,改变和修正父类对抽象化的定义。
  • 实现化角色(Implementor):这个角色给出实现化角色的接口,但是不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
  • 具体实现化角色(ConcreteImplementor):这个角色给出实现化角色接口的具体实现。

抽象化角色就像是一个水杯的手柄,而实现化角色和具体实现化角色就像是水杯的杯身。手柄控制杯身,这就是此模式别名“柄体”的来源。

对象是行为的封装,而行为是由方法实现的。在这个示意性系统里,抽象化等级结构中的类封装了operation()方法;而实现化等级结构中的类封装的是operationImpl()方法。当然,在实际的系统中往往会有多于一个的方法。

抽象化等级结构中的方法通过向对应的实现化对象的委派实现自己的功能,这意味着抽象化角色可以通过向不同的实现化对象委派,来达到动态的转换自己的功能的目的

代码实现:

实现化角色

/**
 * 实现化角色
 */
public abstract class Implementor {
    /**
     * 抽象方法,实现抽象部分需要的某些功能
     */
    abstract void operationImpl();
}

具体实现化操作

/**
 * 具体实现化操作
 */
public class ConcreteImplementorA extends Implementor {
    @Override
    public void operationImpl() {
        //具体操作
        System.out.println("我是具体操作A");
    }
}
public class ConcreteImplementorB extends Implementor {
    @Override
    public void operationImpl() {
        //具体操作
        System.out.println("我是具体化实现操作B");
    }
}

抽象化角色类,它声明了一个方法operation(),并给出了它的实现。这个实现是通过向实现化对象的委派(也就是调用实现化对象的operationImpl()方法)实现的。

/**
 * 抽象化角色类,它声明了一个方法operation(),并给出了它的实现。
 *这个实现是通过向实现化对象的委派(也就是调用实现化对象的operationImpl()方法)实现的。
 */
public abstract class Abstraction {
    protected Implementor implementor;

    public Abstraction(Implementor implementor) {
        this.implementor = implementor;
    }

    /**
     * 示例方法
     */
    public void operation() {
        implementor.operationImpl();
    }
}

修正抽象化角色

/**
 * 修正抽象画角色
 */
public class RefinedAbstraction extends Abstraction {
    public RefinedAbstraction(Implementor implementor) {
        super(implementor);
    }

    /**
     * 其他的操作方法
     */
    public void otherOperation() {

    }
}

业务场景

此时有一个业务场景:发送提示消息。基本上所有带业务流程处理的系统都会有这样的功能,比如OA上有尚未处理完毕的文件,需要发送一条消息提示他。

从业务上看,消息又分为普通消息、加急消息和特急消息多种,不同的消息类型,业务功能处理是不一样的,比如加急消息是在消息上添加加急,而特急消息除了添加特急外,还会做一条催促的记录,多久不完成会继续催促;从发送消息的手段上看,又有系统内短消息、手机短消息、邮件等

不使用模式的解决方案

实现发送普通消息

先考虑实现一个简单点的版本,比如,消息只是实现发送普通消息,发送的方式只实现系统内短消息和邮件。其他的功能,等这个版本完成后,再继续添加。
发送普通消息的结构
消息的统一接口

public interface Message {
    /**
     * 发送消息
     */
    void send (String message, String toUser);
}

系统内短消息示例类

public class CommonMessageSMS implements Message {
    @Override
    public void send(String message, String toUser) {
        System.out.println(String.format("使用系统内部短消息的方法,发送消息 %s 给 %s", message, toUser));
    }
}

邮件短消息示例类

public class CommonMessgeEmail implements Message {
    @Override
    public void send(String message, String toUser) {
        System.out.println(String.format("使用邮件短消息的方法,发送消息 %s 给 %s", message, toUser));
    }
}

实现发送加急消息

发送加急消息同样有两种方式,系统内短消息和邮件方式。但是加急消息的实现不同于普通消息,加急消息会自动在消息上添加加急,然后再发送消息;另外加急消息会提供监控的方法,让客户端可以随时通过这个方法来了解对于加急消息的处理进度。比如,相应的人员是否接收到这个消息,相应的处理工作是否已经展开。因此加急消息需要拓展出一个新的接口,除了基本的发送消息的功能,还需要添加监控功能。

作者:步积
链接:https://www.jianshu.com/p/775cb53a4da2
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

加急消息的接口

public interface UrgencyMessage extends Message {
    /**
     * 监控指定消息的处理过程
     */
    Object watch(String messageId);
}

系统内加急短消息示例类

public class UrgencyMessageSMS implements UrgencyMessage {
    @Override
    public void send(String message, String toUser) {
        message = "加急:" + message;
        System.out.println(String.format("使用系统内部短消息的方法,发送消息 %s 给 %s", message, toUser));
    }

    @Override
    public Object watch(String messageId) {
        //根据消息编码获取消息的状态,组成监控的数据对象,然后返回
        return null;
    }
}

邮件加急短消息示例类

public class UrgencyMessageEmail implements UrgencyMessage {
    @Override
    public void send(String message, String toUser) {
        message = "加急:" + message;
        System.out.println(String.format("使用邮件短消息的方法,发送消息 %s 给 %s", message, toUser));
    }

    @Override
    public Object watch(String messageId) {
        //根据消息编码获取消息的状态,组成监控的数据对象,然后返回
        return null;
    }
}

实现发送特急消息

特急消息不需要查看处理进程,只有没有完成,就直接催促,也就是说,对于特急消息,在普通消息的处理基础上,需要添加催促的功能。
在这里插入图片描述
观察上面的系统结构图,会发现一个很明显的问题,那就是通过这种继承的方式来拓展消息处理,会非常不方便。实现加急消息处理的时候,必须实现系统内短消息和邮件两种处理方式,因为业务处理可能不同,在实现特急消息处理的时候,又必须实现系统内短消息和邮件两种处理方式。这意味着,以后每次拓展一次消息处理,都必须要实现这两种处理方式,这还不算完,如果要添加新的处理方式呢?

添加发送手机消息的处理方式

如果要添加一种新的发送消息的方式,是需要在每一种抽象的具体实现中,都添加发送手机消息的处理的。也就是说,发送普通消息、加急消息、特急消息的处理,都可以通过手机来发送。
在这里插入图片描述
采用通过继承来扩展的实现方式,有个明显的缺点,扩展消息的种类不太容易。不同种类的消息具有不同的业务,也就是有不同的实现。在这种情况下,每一种类的消息,需要实现所有不同的消息发送方式。更可怕的是,如果要新加入一种消息的发送方式,那么会要求所有的消息种类都有加入这种新的发送方式的实现。

那么究竟该如何才能既实现功能,又可以灵活的拓展呢?

使用桥梁模式来解决问题

根据业务的功能要求,业务的变化具有两个维度,一个维度是抽象的消息,包括普通消息、加急消息和特急消息,这几个抽象的消息本身就具有一定的关系,加急消息和特急消息会拓展普通消息;另一个维度是在具体的消息发送方式上,包括系统内短消息、邮件短消息和手机短消息,这几个方式是平等的,可被切换的方式。

实现消息发送的统一接口

public interface MessageImplementor {
    /**
     * 发送消息方法
     * @param message 要发送消息的内容
     * @param toUser 接收人
     */
    void send(String message, String toUser);
}

抽象消息类

public abstract class AbstractMessage {
    /**
     * 持有一个实现部分的对象
     */
    MessageImplementor implementor;

    /**
     * 构造方法,传入实现部分的对象
     * @param implementor 实现部分的对象
     */
    public AbstractMessage(MessageImplementor implementor) {
        this.implementor = implementor;
    }

    /**
     * 发送消息,委派给实现部分的方法
     * @param message 要发送的消息
     * @param toUser 接收人
     */
    public void sendMessage(String message, String toUser) {
        this.implementor.send(message, toUser);
    }
}

普通消息类

public class CommonMessage extends AbstractMessage {
    /**
     * 构造方法,传入实现部分的对象
     *
     * @param implementor 实现部分的对象
     */
    public CommonMessage(MessageImplementor implementor) {
        super(implementor);
    }

    @Override
    public void sendMessage(String message, String toUser) {
        //对于普通消息,直接调用父类方法,发送消息即可
        super.sendMessage(message, toUser);
    }
}

加急消息类

public class UrgencyMessage extends AbstractMessage {
    /**
     * 构造方法,传入实现部分的对象
     *
     * @param implementor 实现部分的对象
     */
    public UrgencyMessage(MessageImplementor implementor) {
        super(implementor);
    }

    @Override
    public void sendMessage(String message, String toUser) {
        message = "加急:" + message;
        super.sendMessage(message, toUser);
    }

    /**
     * 扩展它自己的功能,监控某个消息的处理状态
     * @param messageId 消息编码
     * @return 监控到的消息的处理状态
     */
    public Object watch(String messageId) {
        //根据给出的消息编码查询消息的处理状态,组织成监控的处理状态,然后返回。
        return null;
    }
}

系统内短消息的实现类

public class MessageSMS implements MessageImplementor {
    @Override
    public void send(String message, String toUser) {
        System.out.println(String.format("使用系统内部短消息的方法,发送消息 %s 给 %s", message, toUser));
    }
}

邮件短消息的实现类

public class MessageEmail implements MessageImplementor {
    @Override
    public void send(String message, String toUser) {
        System.out.println(String.format("使用邮件短消息的方法,发送消息 %s 给 %s", message, toUser));
    }
}

客户端类

public class Client {
    public static void main (String[] args) {
        MessageImplementor implementor = new MessageSMS();
        AbstractMessage abstractMessage = new CommonMessage(implementor);
        abstractMessage.sendMessage("加班申请速批", "陈总");

        implementor = new MessageEmail();
        abstractMessage = new UrgencyMessage(implementor);
        abstractMessage.sendMessage("加班申请速批", "陈总");
    }
}

通过上面的例子会发现,采用桥梁模式来实现,抽象部分和实现部分分离开了,可以相互独立的变化,而不会相互影响。因此在抽象部分增加新的消息处理(特急消息),对发送消息的实现部分是没有影响的;反过来增加发送消息的方式(手机短消息),对消息处理部分也是没有影响的。

参考:

https://www.jianshu.com/p/775cb53a4da2
https://www.cnblogs.com/ruaa/p/13038076.html
https://blog.csdn.net/paincupid/article/details/43614029

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

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

相关文章

Es直方图聚合--date_histogram

文章目录 1、背景2、bucket_key如何计算3、前置知识4、日历和固定时间间隔 4.1 Calendar intervals 日历间隔4.2 Fixed intervals 固定间隔 5、数据准备 5.1 准备mapping5.2 准备数据 6、聚合案例 6.1 dsl6.2 java代码6.3 聚合结果 7、完整代码8、参考文档 1、背景 此处来简单学…

云计算UPS监控,怎么办?

在大型数据机房中,UPS系统扮演着关键的角色,为计算机和网络设备提供可靠的电力备份。由于数据机房的规模庞大且关键性强,监控UPS系统的可靠性和效率至关重要。 UPS监控可以提供实时的电池状态、负载信息、电网电压等监测数据,并能…

c++中assert

参考:https://blog.csdn.net/bitcarmanlee/article/details/124283683 1.什么是assert assert,中文翻译为断言,注意是一个宏定义,不是函数。 c中,要使用assert,可以将cassert头文件include进来,而cassert最…

路径规划算法:基于孔雀优化的路径规划算法- 附代码

路径规划算法:基于孔雀优化的路径规划算法- 附代码 文章目录 路径规划算法:基于孔雀优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法孔雀…

【HCIA】09.STP

STP的选举之发波原理 设备启动之后,经过选举会分别显示出它们的等级(最强,次强,最弱)选出等级之后,两两设备开始互相发波,等级强的设备会将弱的设备的光顶回去此时两两设备之间的波就是单方向的…

家政服务小程序软件解决方案

家政服务小程序软件是近年来随着人们对家政服务需求的增长而逐渐兴起的一种数字化服务解决方案。通过小程序软件,用户可以轻松预约家政服务,包括保姆、月嫂、钟点工等,而且价格透明、服务规范,大大提高了用户对家政服务的满意度。…

神经网络结构可视化-netron

网址:https://netron.app/ 点选择模型,将oonx文件拉到netron界面,即可 输出; 如何将pytorch模型转换为onnx的格式? 在测试(训练好的模型)里输入代码 to_onnx(model, 3, 28, 28, output/params.onnx)其…

【Linux】十分钟理解动静态库

目录 一 前置概念二 静态库2.12.22.3放入指定路径2.4 第三方库的使用 四 动态库3.1 环境变量3.2 软链接方案3.3 配置文件方案 一 前置概念 我们在VS2022下安装开发环境实际上就是安装编译器软件、安装要开发的语言配套的库和头文件。我们使用编译器有语法的自动提醒功能&#…

python常用库之colorama (python命令行界面打印怎么加颜色)

文章目录 python常用库之colorama (python命令行界面打印怎么加颜色)背景colorama介绍colorama使用colorama打印红色闪烁打印颜色组合 python常用库之colorama (python命令行界面打印怎么加颜色) 背景 在Python开发项目过程中,为了方便调试代码,经常会…

Java中Map中10w条数据用什么循环性能最好呢?

加油,新时代打工人! 1、java中List集合三种获取集合元素方式 2、Java中Map使用增强for循环和迭代器获取key和value 选择合适的循环方式,让性能最优! public class Test2 {public static void main(String[] args) {//初始化 10w…

vue upload 上传下载

目录 上传 下载 对象/文件流 download处理返回 文件流 axios.post 封装axios 1.请求设置类型responseType: blob 2.若有请求拦截(直接返回即可) 3.download 4.请求下载 相关基础 blob MIME vue 实现文件上传、下载的方法 - 掘金 上传 submitAddFile(){var form…

基础算法-【离散化】

离散化的本质:是建立了一段数列到自然数之间的映射关系(value -> index),通过建立新索引,来缩小目标区间,使得可以进行一系列连续数组可以进行的操作比如二分,前缀和等… 相应的算法模板: v…

【Linux】—— 进程地址空间

序言: 在上篇中,我们讲解了关于进程优先级的概念。本期,我将给大家介绍的是关于进程地址空间的话题。 目录 (一)程序地址空间回顾 (二)代码演示 (三)进程地址空间的引…

【力扣算法08】之 5. 最长回文子串 python

文章目录 问题描述示例1示例2提示 思路分析代码分析完整代码详细分析 运行效果截图调用示例运行结果 完结 问题描述 给你一个字符串 s,找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。 示例1 输入:s…

【网络编程】Linux服务器程序规范相关内容

文章目录 1、日志1.1、syslog()函数 2、用户信息2.1、UID、EUID、GID、EGID 3、进程间关系3.1、进程组3.2、会话 4、服务器程序后台化(守护进程) 1、日志 Linux提供一个守护进程rsyslogd来处理系统日志,系统日志中包括用户进程产生的日志以及…

VUE2基础-Vue实例

Vue 实例 创建一个 Vue 实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的: var vm new Vue({// 选项 }) 虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名…

交换:交换机相关技术(二层技术)

目录 VLAN:虚拟局域网 VLAN种类: 接口分配链路类型 接口划分VLAN 跨网段的通讯 VLAN:虚拟局域网 LAN :局域网 MAN:城域网 WAN:广域网 1.一个VLAN相当于一个广播域 VLAN:通过路由器和交换机…

PADS Logic如何对原理图页面进行操作呢?

在绘制复杂的原理图时,会根据功能模块来将原理图进行分页处理,在绘制原理图过程中,会对原理图页面进行一些处理,操作方法如下所列: 1、页面拷贝与粘贴:在原理图中选中需要复制的元件走线等,左击…

Git常用命令及在Idea中如何使用创建分支等,详讲带图[保姆级]

文章目录 Git在Git命令行中执行下面命令:设置基本信息获取Git仓库Git 工作区 暂存区 版本库概念工作状态远程仓库操作分支操作标签分类 Idea中使用推送到远程仓库(提交并且推送)分支操作 Git 在Git命令行中执行下面命令: 设置基本信息 设置用户信息 git config --global use…

阿里巴巴开源的Spring Cloud Alibaba手册在GitHub上火了,完整版开放下载

“微服务架构经验你有吗?” 前段时间一个朋友去面试,阿里面试官一句话问倒了他。实际上,不在BAT这样的大厂工作,是很难接触到支撑千亿级流量微服务架构项目的。但也正是这种难得,让各个大厂都抢着要这样的人才&#x…