手撕设计模式之消息推送系统——桥接模式

1.业务需求

​ 大家好,我是菠菜啊,好久不见,今天给大家带来的是——桥接模式。老规矩,在介绍这期内容前,我们先来看看这样的需求:我们现在要做一个消息推送系统,实现纯文本消息和html格式消息的推送,推送方式支持email、短信,我们该怎么实现?

请添加图片描述

2.代码实现

Talk is cheap,show me your code.

初版实现思路:

​ 假设我们有一个父类Message类(消息类型),从它扩展出俩个子类:TextMessage、HtmlMessage,又要支持email、短信推送方式,那么我们需要共创建4个子类才能覆盖所有组合。

请添加图片描述

初版代码如下:

//消息基类
public abstract class Message {abstract   void sendMessage(String message);
}
//文本消息类
public class TextMessage extends  Message{@Overridevoid sendMessage(String message) {System.out.println("TextMessage:"+message);}
}
//html消息类
public class HtmlMessage extends  Message{@Overridevoid sendMessage(String message) {System.out.println("HtmlMessage:"+message);}
}
public class HtmlEmailMessage extends HtmlMessage{@Overridevoid sendMessage(String message) {super.sendMessage(message);System.out.println("send HtmlMessage by eamil");}
}
public class HtmlSmsMessage extends HtmlMessage{@Overridevoid sendMessage(String message) {super.sendMessage(message);System.out.println("send HtmlMessage by sms");}
}
public class TextEmailMessage extends TextMessage{@Overridevoid sendMessage(String message) {super.sendMessage(message);System.out.println("send TextMessage by eamil");}
}
public class TextSmsMessage extends TextMessage{@Overridevoid sendMessage(String message) {super.sendMessage(message);System.out.println("send TextMessage by sms");}
}
public class Client {public static void main(String[] args) {Message message = new TextSmsMessage();message.sendMessage("hello world");Message message2 = new HtmlEmailMessage();message2.sendMessage("<html> <h>hello world </h></html>");}
}

输出结果:

在这里插入图片描述

思考:

​ 上述每添加一种消息类型或者消息推送方式,都要新增一个维度的子类,导致类爆炸。而且我们发现消息类型和消息推送方式是俩种独立维度,我们不应该用继承的这种方式去拓展,那么该怎么解决呢?

3.代码优化

//发送方式
public interface MessageSender {void send(String message);
}
//短信发送方式
public class SmsMessageSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("使用sms发送消息:" + message);}
}
//eamil发送方式
public class EmailMessageSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("使用email发送消息:" + message);}
}
//消息类
public abstract class Message2 {protected MessageSender messageSender;public Message2(MessageSender messageSender) {this.messageSender = messageSender;}abstract   void sendMessage(String message);
}
//文本消息类
public class TextMessage2 extends  Message2{public TextMessage2(MessageSender messageSender) {super(messageSender);}@Overridevoid sendMessage(String message) {System.out.println("TextMessage:"+message);messageSender.send(message);}
}
//html消息类
public class HtmlMessage2 extends  Message2{public HtmlMessage2(MessageSender messageSender) {super(messageSender);}@Overridevoid sendMessage(String message) {System.out.println("HtmlMessage:"+message);messageSender.send(message);}
}

输出结果:

在这里插入图片描述

实现代码结构图:
在这里插入图片描述

思考:

​ 上述将发送消息方式抽象出来,消息类中消息的发送方式委托给MessageSender,通过组合的方式实现消息类型(文本、HTML、模板、紧急)和发送渠道(邮件、短信、推送、微信)这两个维度的独立变化,职责清晰,扩展性强。这个设计模式完全体现了设计原则中的合成复用原则(Composite Reuse Principle)“优先使用对象组合(Composition),而不是类继承(Inheritance)来实现代码复用。”

4.定义与组成

请添加图片描述


定义:

桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们可以独立地变化。

核心思想
通过组合替代继承,将抽象(功能层次)与实现(平台层次)解耦,解决多维变化导致的类爆炸问题。

组成部分:

  • 抽象部分(Abstraction):定义高层控制逻辑的接口,并且持有实现部分的引用

  • 精确抽象(Refined Abstraction):扩展抽象功能的子类

  • 实现者接口(Implementor):定义底层实现的契约

  • 具体实现者(Concrete Implementor):具体的底层实现

5.典型应用

1)JDBC驱动程序(源码简化)

//实现者接口 - Driver
public interface Driver {Connection connect(String url, Properties info) throws SQLException;boolean acceptsURL(String url);
}
//具体实现者 - MySQLDriver
public class MySQLDriver implements Driver {@Overridepublic Connection connect(String url, Properties info) {if (!acceptsURL(url)) return null;// 实际建立物理连接Socket socket = new Socket(extractHost(url), extractPort(url));return new MySQLConnectionImpl(socket);}private String extractHost(String url) { /* 解析主机名 */ }private int extractPort(String url) { /* 解析端口号 */ }
}
//抽象接口 - Connection
public interface Connection extends AutoCloseable {Statement createStatement() throws SQLException;PreparedStatement prepareStatement(String sql) throws SQLException;// ... 其他抽象方法
}
//精确抽象 - MySQLConnectionImpl
class MySQLConnectionImpl implements Connection {private final Socket socket;private final OutputStream out;private final InputStream in;public MySQLConnectionImpl(Socket socket) {this.socket = socket;this.out = socket.getOutputStream();this.in = socket.getInputStream();}@Overridepublic Statement createStatement() {return new MySQLStatementImpl(this);}// 实际发送SQL命令到MySQL服务器void sendCommand(String command) {out.write(command.getBytes());out.flush();}// ... 其他具体实现
}
//桥接点 - DriverManager
public class DriverManager {private static final List<Driver> drivers = new CopyOnWriteArrayList<>();public static void registerDriver(Driver driver) {drivers.add(driver);}public static Connection getConnection(String url, String user, String pass) {for (Driver driver : drivers) {if (driver.acceptsURL(url)) {Properties props = new Properties();props.setProperty("user", user);props.setProperty("password", pass);return driver.connect(url, props);}}throw new SQLException("No suitable driver found");}
}

2)GUI开发(AWT/Swing)

windows类中有抽象的窗口接口,并有一个画窗口的方法,实现不同平台画窗口功能委托给具体的平台实现


// 抽象:Window
public class Window {private WindowImpl impl;  // 桥接关键public void draw() {impl.deviceDraw();  // 委托给具体平台实现}
}// 实现:不同OS的图形实现
interface WindowImpl {void deviceDraw();
}class WindowsWindowImpl implements WindowImpl {...}
class MacWindowImpl implements WindowImpl {...}

6.适用场景

场景特征示例
存在多个独立变化维度图形形状 × 渲染方式
需要运行时切换实现动态切换支付渠道
避免永久绑定抽象与实现JDBC连接不同数据库
类层次结构爆炸避免创建 N×M 子类组合

经验法则:当听到"需要根据不同平台/方式做不同实现"时,优先考虑桥接模式

技术需要沉淀,同样生活也是~
个人链接:博客,欢迎一起交流

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

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

相关文章

Java 大厂面试题 -- JVM 垃圾回收机制大揭秘:从原理到实战的全维度优化

最近佳作推荐&#xff1a; Java 大厂面试题 – JVM 面试题全解析&#xff1a;横扫大厂面试&#xff08;New&#xff09; Java 大厂面试题 – 从菜鸟到大神&#xff1a;JVM 实战技巧让你收获满满&#xff08;New&#xff09; Java 大厂面试题 – JVM 与云原生的完美融合&#xf…

图机器学习(9)——图正则化算法

图机器学习&#xff08;9&#xff09;——图正则化算法1. 图正则化方法2. 流形正则化与半监督嵌入3. 神经图学习4. Planetoid1. 图正则化方法 浅层嵌入方法已经证明&#xff0c;通过编码数据点间的拓扑关系可以构建更鲁棒的分类器来处理半监督任务。本质上&#xff0c;网络信息…

视频动态范围技术演进:从SDR到HDR的影像革命

一、动态范围技术基础认知 1.1 人眼视觉特性与动态范围 人眼的动态感知范围可达106:1&#xff08;0.0001-105 cd/m&#xff09;&#xff0c;远超传统显示设备能力。视网膜通过虹膜调节&#xff08;物理孔径&#xff09;与光化学反应&#xff08;光敏蛋白分解&#xff09;实现16…

基于LAMP环境的校园论坛项目

1.配置本地仓库a.修改主机名为自己姓名全拼[rootserver ~]# hostnamectl set-hostname jun [rootserver ~]# bash [rootjun ~]# 运行结果图如下图所示&#xff1a;b.光盘挂载到/mnt目录下[rootjun yum.repos.d]# mount /dev/sr0 /mnt mount: /mnt: WARNING: source write-prote…

在物联网系统中时序数据库和关系型数据库如何使用?

在物联网系统中&#xff0c;时序数据库&#xff08;TSDB&#xff09;和关系型数据库&#xff08;RDBMS&#xff09;的存储顺序设计需要根据数据特性、业务需求和系统架构综合考虑。以下是典型的设计方案和逻辑顺序&#xff1a;1. 常见存储顺序方案 方案一&#xff1a;先写时序数…

django安装、跨域、缓存、令牌、路由、中间件等配置

注意&#xff1a;如果是使用 PyCharm 编程工具就不用创建虚拟化&#xff0c;直接打开 PyCharm 选择新建的目录直接调过下面的步骤11. 项目初始化如果不是用 PyCharm 编辑器就需要手动创建虚拟环境在项目目录cmd&#xff0c;自定义名称的虚拟环境# 激活虚拟环境 python -m venv …

时间的弧线,逻辑的航道——标准单元延迟(cell delay)的根与源

时序弧 在这篇文章中&#xff0c;我们将讨论影响标准单元延迟的因素。在开始讨论之前&#xff0c;我们需要先了解一下什么是时序弧 (Timing Arcs)&#xff1a; 时序弧 (Timing Arcs)&#xff1a; 时序弧代表了信号从一个输入流向一个输出的方向。它存在于组合逻辑和时序逻辑中&…

《透视定轴:CSS 3D魔方中视觉层级的秩序法则》

当CSS的代码编织出一个能自由旋转的3D魔方&#xff0c;六个色彩各异的面在空间中翻转、重叠时&#xff0c;最考验技术的并非旋转动画的流畅度&#xff0c;而是每个面在任意角度下都能保持符合现实逻辑的前后关系。为何有时某个面会突兀地“穿透”另一个面&#xff1f;为何旋转到…

RTL编程中常用的几种语言对比

以下是RTL&#xff08;寄存器传输级&#xff09;编程中常用的几种硬件描述语言&#xff08;HDL&#xff09;及其核心差异的对比分析。RTL编程主要用于数字电路设计&#xff0c;通过描述寄存器间的数据传输和逻辑操作实现硬件功能。以下内容综合了行业主流语言的技术特性与应用场…

前端面试题(HTML、CSS、JavaScript)

目录 一、HTML src与href区别 对html语义化理解 语义化标签有哪些&#xff1f; script中的defer与async区别 行内元素与块级元素有哪些&#xff1f; canvas与svg区别 SEO优化 html5新特性 二、CSS 盒模型 选择器优先级 伪元素与伪类 隐藏元素几种方式 水平/垂直…

Linux-线程控制

线程等待pthread_join()pthread_join 是 Linux 系统中用于线程同步的重要函数&#xff0c;主要作用是等待指定线程结束并回收其资源。基本功能- 阻塞当前调用线程&#xff0c;直到目标线程执行结束。 - 回收目标线程的资源&#xff0c;避免产生“僵尸线程”。 - 可选地获取目标…

RAG优化秘籍:基于Tablestore的知识库答疑系统架构设计

目录一、技术架构设计二、双流程图解析横向架构对比纵向核心流程三、企业级代码实现Python检索核心TypeScript前端接入YAML部署配置四、性能对比验证五、生产级部署方案六、技术前瞻分析附录&#xff1a;完整技术图谱一、技术架构设计 原创架构图 #mermaid-svg-3Ktoc4oH4xlbD6…

i.mx8 RTC问题

项目场景&#xff1a;需要增加外置RTC&#xff0c;保证时间的精准。问题描述&#xff1a;基本情况&#xff0c;外置i2c接口的RTC&#xff0c;注册、读写都正常&#xff0c;但是偶发性重启后&#xff0c;系统时间是2022&#xff0c;rtc时间是1970&#xff0c;都像是恢复了默认时…

数据集相关类代码回顾理解 | utils.make_grid\list comprehension\np.transpose

目录 utils.make_grid list comprehension np.transpose utils.make_grid x_gridutils.make_grid(x_grid, nrow4, padding2) make_grid 函数来自torchvision的utils模块&#xff0c;用于图像数据可视化&#xff0c;将一批图像排列成一个网格。 x_grid&#xff1a;四维图像…

C#中Static关键字解析

本文仅作为参考大佬们文章的总结。 Static关键字是C#语言中一个基础而强大的特性&#xff0c;它能够改变类成员的行为方式和生命周期。本文系统性总结static关键字的各类用法、核心特性、适用场景以及需要注意的问题&#xff0c;以帮助掌握这一重要概念。 一、Static关键字概…

通用综合文字识别联动 MES 系统:OCR 是数据流通的核心

制造业的 MES 系统需实时整合生产数据以调控流程&#xff0c;但车间的工单、物料标签、质检报告等多为纸质或图片形式&#xff0c;传统人工录入不仅滞后&#xff0c;还易出错&#xff0c;导致 MES 系统数据断层。通用综合文字识别借助 OCR 技术&#xff0c;成为连接这些信息与 …

【Linux 学习指南】网络编程基础:从 IP、端口到 Socket 与 TCP/UDP 协议详解

文章目录&#x1f4dd;理解源IP地址和目的IP地址&#x1f320; 认识端口号&#x1f309;端口号范围划分&#x1f309;理解"端口号"和"进程ID"&#x1f309;理解源端口号和目的端口号&#x1f309;理解socket&#x1f320;传输层的典型代表&#x1f309;认识…

React+Next.js+Tailwind CSS 电商 SEO 优化

一、项目背景与技术选型​1. 原始痛点​项目最初基于纯 React 开发&#xff08;SPA 架构&#xff09;&#xff0c;存在三个致命问题&#xff1a;​搜索引擎爬虫无法有效抓取动态渲染的商品详情、分类页内容&#xff1b;​单页面应用 难以实现页面级的 meta 定制&#xff0c;关键…

Process Lasso:提升电脑性能的得力助手

在日常使用电脑的过程中&#xff0c;我们常常会遇到这样的问题&#xff1a;电脑运行缓慢、程序响应迟缓、多任务处理时卡顿不断。这些问题不仅影响工作效率&#xff0c;还让人感到非常烦躁。其实&#xff0c;这些问题很多时候是因为电脑的进程管理不够优化。而Process Lasso正是…

AI驱动的大前端内容创作与个性化推送:资讯类应用实战指南

在信息爆炸的时代&#xff0c;资讯类应用面临两大核心挑战&#xff1a;一是如何高效生产海量优质内容&#xff0c;二是如何让用户从海量信息中快速获取感兴趣的内容。AI技术的介入正在重构资讯类应用的开发模式&#xff0c;从内容生产到用户触达形成全链路智能化。本文将从开发…