中介者模式的理解和实践

一、中介者模式概述

        中介者模式(Mediator Pattern),也称为调解者模式或调停者模式,是一种行为设计模式。它的核心思想是通过引入一个中介者对象来封装一系列对象之间的交互,使得这些对象不必直接相互作用,从而达到松散耦合的效果。这种设计模式主要用于降低系统中对象之间的耦合度,提高系统的灵活性和可维护性。

        中介者模式定义了一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,并且可以独立地改变它们之间的交互。在这个模式中,各个对象不再直接相互引用和通信,而是通过中介者来进行间接通信。这样,不仅降低了对象之间的依赖关系,还简化了对象之间的交互逻辑。

 

二、中介者模式的结构

        中介者模式通常包含以下几个关键角色:

  1. 抽象中介者(Mediator):定义一个接口,该接口用于定义各同事(Colleague)对象之间进行通信需要的方法。
  2. 具体中介者(ConcreteMediator):实现抽象中介者接口,通过协调各个同事对象来实现协作行为,了解并维护它的各个同事,它维持了对各个同事对象的引用。
  3. 抽象同事类(Colleague):定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。
  4. 具体同事类(ConcreteColleague):实现抽象同事类,每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信。

三、中介者模式的理解

  1. 降低耦合度:通过引入中介者对象,各个对象之间的直接依赖关系被减少,使得系统结构更加灵活和可扩展。
  2. 简化交互:中介者模式将对象之间的多对多交互关系转换为中介者与各个对象之间的一对多关系,简化了对象之间的交互逻辑。
  3. 易于维护和扩展:由于对象之间的交互被集中到中介者对象中,因此当需要修改或扩展对象之间的交互时,只需修改中介者对象,而不需要修改各个对象的代码。

        然而,中介者模式也有其局限性。如果系统中存在大量的同事类,中介者可能会变得非常复杂,难以维护。因此,在实际应用中需要权衡利弊,根据具体情况决定是否采用该模式。

四、中介者模式的实践

        下面我们通过具体的Java代码示例来展示中介者模式的实践应用。

示例一:简单的聊天系统

        假设我们要设计一个可以让多人参与进去的聊天室,该聊天室需要实现发送消息和新增用户的功能。

        首先,我们定义聊天室的接口ChatRoom,其中包含两个方法sendMessageaddUser,分别代表发送消息和新增用户。这里的ChatRoom就是抽象的中介者类。

public interface ChatRoom {void sendMessage(String msg, String userId);void addUser(User user);
}


        然后,我们创建一个ChatRoom的实现类ChatRoomImpl,使用addUser来添加需要聊天的用户对象,同时这里再使用一个Map来保存添加时需要用来进行通信的对象列表。在发送消息sendMessage的方法中,我们通过userId指定某个对象来接收消息。

import java.util.HashMap;
import java.util.Map;public class ChatRoomImpl implements ChatRoom {private Map<String, User> usersMap = new HashMap<>();@Overridepublic void sendMessage(String msg, String userId) {User u = usersMap.get(userId);u.receive(msg);}@Overridepublic void addUser(User user) {this.usersMap.put(user.getId(), user);}
}


        接下来,我们定义一个抽象组件类User,它持有一个ChatRoom的引用,并声明了发送消息send和接收消息receive的抽象方法。

public abstract class User {private ChatRoom mediator;private String id;private String name;public User(ChatRoom room, String id, String name) {this.mediator = room;this.name = name;this.id = id;}public abstract void send(String msg, String userId);public abstract void receive(String msg);public ChatRoom getMediator() {return mediator;}public String getId() {return id;}public String getName() {return name;}
}


        然后,我们实现一个具体的组件类ChatUser,并实现发送消息send和接收消息receive的方法。

public class ChatUser extends User {public ChatUser(ChatRoom room, String id, String name) {super(room, id, name);}@Overridepublic void send(String msg, String userId) {getMediator().sendMessage(msg, userId);}@Overridepublic void receive(String msg) {System.out.println(getName() + "收到消息: " + msg);}
}


        最后,我们在客户端代码中创建中介者和同事对象的实例,并将它们关联起来。

public class ChatDemo {public static void main(String[] args) {ChatRoom chatRoom = new ChatRoomImpl();User user1 = new ChatUser(chatRoom, "1", "张三");User user2 = new ChatUser(chatRoom, "2", "李四");chatRoom.addUser(user1);chatRoom.addUser(user2);user1.send("你好, 李四", "2");user2.send("你好, 张三", "1");}
}


        运行上述代码,输出结果如下:

李四收到消息: 你好, 李四
张三收到消息: 你好, 张三


        通过这个示例,我们可以看到,聊天系统中的用户对象(User)通过中介者(ChatRoom)来进行间接通信,而不需要直接引用对方。这样,不仅降低了用户对象之间的耦合度,还简化了用户之间的交互逻辑。

示例二:多架飞机降落

        假设有多架飞机需要跑道降落,塔台作为中介者指挥飞机降落。每一架飞机都是一个组件,拥有一些相同的行为,塔台作为中介者负责指挥飞机的降落。

        首先,我们定义抽象中介者接口TowerMediator,它包含通知方法notify、注册方法register和移除方法remove

public interface TowerMediator {void notify(String type, String track);void register(AirplaneComponent colleague);void remove(AirplaneComponent colleague);
}


        然后,我们实现具体的中介者类AirlinerConcreteMediator,它持有一个List来管理组件对象(即飞机)。

import java.util.ArrayList;
import java.util.List;public class AirlinerConcreteMediator implements TowerMediator {private List<AirplaneComponent> colleagues = new ArrayList<>();@Overridepublic void notify(String type, String track) {for (AirplaneComponent airplaneComponent : colleagues) {if (!type.equalsIgnoreCase(airplaneComponent.getAirplaneType())) {continue;}airplaneComponent.landing(track);}}@Overridepublic void register(AirplaneComponent colleague) {colleagues.add(colleague);}@Overridepublic void remove(AirplaneComponent colleague) {colleagues.remove(colleague);}
}


        接下来,我们定义抽象组件类AirplaneComponent,它持有一个中介者对象的引用,并声明了一个抽象方法landing

public abstract class AirplaneComponent {public String airplaneType;private TowerMediator towerMediator;public AirplaneComponent(String airplaneType, TowerMediator towerMediator) {this.airplaneType = airplaneType;this.towerMediator = towerMediator;}public abstract void landing(String track);public TowerMediator getTowerMediator() {return towerMediator;}
}


        然后,我们实现具体的组件类AirlinerConcreteComponent,它代表客机。

public class AirlinerConcreteComponent extends AirplaneComponent {public AirlinerConcreteComponent(String airplaneType, TowerMediator towerMediator) {super(airplaneType, towerMediator);}@Overridepublic void landing(String track) {System.out.println(airplaneType + "正在跑道" + track + "上降落");}
}


        最后,我们在客户端代码中创建中介者和组件对象的实例,并将它们关联起来。

// 客户端代码
public class AirportDemo {public static void main(String[] args) {// 创建中介者(塔台)TowerMediator towerMediator = new AirlinerConcreteMediator();// 创建具体的组件(飞机),并注册到中介者AirplaneComponent airplane1 = new AirlinerConcreteComponent("波音737", towerMediator);AirplaneComponent airplane2 = new AirlinerConcreteComponent("空客A320", towerMediator);AirplaneComponent airplane3 = new AirlinerConcreteComponent("波音787", towerMediator);// 通知中介者进行降落安排(这里假设有两个跑道:Runway 1 和 Runway 2)towerMediator.notify("波音737", "Runway 1");towerMediator.notify("空客A320", "Runway 2");towerMediator.notify("波音787", "Runway 1"); // 假设波音787可以等待或使用另一个空闲跑道// 注意:由于中介者内部已经维护了组件的列表,因此不需要显式地移除组件,// 除非在程序的某个阶段需要动态地卸载组件。}
}

        在这个示例中,AirportDemo类的main方法是客户端代码,它创建了中介者AirlinerConcreteMediator的实例,并创建了多个具体的组件(飞机)实例,这些实例都被注册到了中介者中。然后,通过调用中介者的notify方法,模拟了飞机请求降落的过程,并分配了跑道。

        运行AirportDemo类的main方法,您将看到类似以下的输出:

波音737 正在跑道 Runway 1 上降落
空客A320 正在跑道 Runway 2 上降落
波音787 正在跑道 Runway 1 上降落

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

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

相关文章

MySQL-DQL之数据多表操作

文章目录 一. 多表操作1. 表与表之间的关系2. 外键约束3. 创建外键约束表(一对多操作) 二. 多表查询1. 多表查询① 交叉连接查询(基本不会使用-得到的是两个表的乘积) [了解]&#xff08;不要记住&#xff09;② 交集运算&#xff1a;内连接查询(join)③ 差集运算&#xff1a;外…

Qt之自定义动态调控是否显示日志

创作灵感 最近在芯驰x9hp上开发仪表应用。由于需要仪表警告音&#xff0c;所以在该平台上折腾并且调试仪表声音的时候&#xff0c;无意间发现使用&#xff1a; export QT_DEBUG_PLUGINS1 可以打印更详细的调试信息。于是想着自己开发的应用也可以这样搞&#xff0c;这样更方便…

Nanolog起步笔记-9-log解压过程(3)寻找meta续

Nanolog起步笔记-9-log解压过程-3-寻找meta续 当前的目标新的改变decompressNextLogStatementmetadata查看业务面的log语句注释掉 runBenchmark();改过之后&#xff0c;2条记录之后&#xff0c;这里就直接返回了 小结 当前的目标 没有办法&#xff0c;还要继续。 当前的目标&a…

最小二乘法拟合出二阶响应面近似模型

背景&#xff1a;根据样本试验数据拟合出二阶响应面近似模型&#xff08;正交二次型&#xff09;&#xff0c;并使用决定系数R和调整的决定系数R_adj来判断二阶响应面模型的拟合精度。 1、样本数据&#xff08;来源&#xff1a;硕士论文《航空发动机用W形金属密封环密封性能分析…

《操作系统 - 清华大学》6 -7:局部页面置换算法:Belady现象

文章目录 1. 定义2. LRU、FIFO和Clock的比较 1. 定义 局部页面置换算法的特点是针对一个正在运行的程序&#xff0c;它访问内存的情况&#xff0c;访问页的情况&#xff0c;来决定应该采取什么样策略&#xff0c;把相应的页替换出去&#xff0c;站在算法本身角度来考虑置换哪个…

【开源免费】基于SpringBoot+Vue.JS在线办公系统(JAVA毕业设计)

本文项目编号 T 001 &#xff0c;文末自助获取源码 \color{red}{T001&#xff0c;文末自助获取源码} T001&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

05-标准库开发-STM32-IIC协议

七、STM32中IIC协议 概述 Inter-Integrated Circuit (IIC)&#xff0c;也常称为I2C&#xff08;I squared C&#xff09;&#xff0c;是一种同步、串行、半双工通信总线协议。它主要用于连接低速外围设备到处理器或微控制器上&#xff0c;如MPU6050姿态传感器、OLED显示屏、存…

【linux系统】基础开发工具(yum、Vim)

1. 软件包管理器 1.1 什么是软件包 在Linux下安装软件, ⼀个通常的办法是下载到程序的源代码, 并进⾏编译, 得到可执⾏程序. 但是这样太麻烦了, 于是有些⼈把⼀些常⽤的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在⼀个服务器上, 通过包管理器可以很⽅便的…

UFUG2601_project_Fall2024 MiniDB Project

PS&#xff1a;如果读过题了可以跳过题目描述直接到题解部分 链接&#xff1a;UFUG2601_project_Fall2024 MiniDB Project 文章目录 题目题解声明可完成操作运行逻辑大致思路数据存储数据类型数据名称 命令输入文件读入命令读入 操作2.1 Create Database and Use Database2.2 C…

this version of the Java Runtime only recognizes class file versions up to 52.0

问题描述 Exception in thread "main" java.lang.UnsupportedClassVersionError: com/xxx/Main has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versi…

Tr0ll: 1 Vulnhub靶机渗透笔记

Tr0ll: 1 本博客提供的所有信息仅供学习和研究目的&#xff0c;旨在提高读者的网络安全意识和技术能力。请在合法合规的前提下使用本文中提供的任何技术、方法或工具。如果您选择使用本博客中的任何信息进行非法活动&#xff0c;您将独自承担全部法律责任。本博客明确表示不支…

CAP定理

2.1 CAP 定理的由来与证明 CAP 定理是计算机科学界的“铁律”&#xff0c;最早由 Eric Brewer 提出&#xff0c;后来被正式证明&#xff1a; 分布式系统里&#xff0c;一致性&#xff08;C&#xff09;、可用性&#xff08;A&#xff09;、分区容错性&#xff08;P&#xff09…

【flutter】webview下载文件方法集锦

说明&#xff1a;android的webview是不支持下载的&#xff01;&#xff01;&#xff01; 所以我们需要监听下载接口 然后手动执行下载操作&#xff0c;分为三种类型 直接打开浏览器下载&#xff08;最简单&#xff09;&#xff0c;但是一些下载接口需要cookie信息时不能满足 …

Java版-图论-最短路-Floyd算法

实现描述 网络延迟时间示例 根据上面提示&#xff0c;可以计算出&#xff0c;最大有100个点&#xff0c;最大耗时为100*wi,即最大的耗时为10000&#xff0c;任何耗时计算出来超过这个值可以理解为不可达了&#xff1b;从而得出实现代码里面的&#xff1a; int maxTime 10005…

SQL注入基础入门篇 注入思路及常见的SQL注入类型总结

目录 前言一、了解mysql数据库1、了解sql增删改查2、了解sql查询 二、sql注入基础三、学习sql注入漏洞1、union注入1、判断数字型注入还是字符型型注入&#xff1a;2、判断闭合方式&#xff08;字符型注入&#xff09;&#xff1a;3、判断回显位4、查询库名&#xff0c;表名&am…

基于Spring Boot库存管理系统

文末获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 基于Spring Boot库存管理系统 当下&#xff0c;如果还依然使用纸质文档来记录并且管理相关信息&#xff0c;可能会出现很多问题&#xff0c;比如原始文件的丢失&#xff0c;因为采用纸质文档&#xff0c…

JSSIP的使用及问题(webRTC,WebSockets)

简介 项目中有一个需要拨打电话的功能&#xff0c;要求实时的进行音频接听&#xff0c;并且可以在电话接听或者挂断等情况下做出相应的操作。jssip作为一个强大的实现实时通信的javascript库&#xff0c;这不门当户对了嘛。 jssip&#xff08;官网&#xff1a; JsSIP - the J…

【Cadence32】PCB多层板电源、地平面层创建心得➕CM约束管理器Analyze分析显示设置➕“DP”报错DRC

【转载】Cadence Design Entry HDL 使用教程 【Cadence01】Cadence PCB Edit相对延迟与绝对延迟的显示问题 【Cadence02】Allegro引脚焊盘Pin设置为透明 【Cadence03】cadence不小心删掉钢网层怎么办&#xff1f; 【Cadence04】一般情况下Allegro PCB设计时的约束规则设置&a…

Java阶段三06

第3章-第6节 一、知识点 理解MVC三层模型、理解什么是SpringMVC、理解SpringMVC的工作流程、了解springMVC和Struts2的区别、学会使用SpringMVC封装不同请求、接收参数 二、目标 理解MVC三层模型 理解什么是SpringMVC 理解SpringMVC的工作流程 学会使用SpringMVC封装请求…

C/C++流星雨

系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C/C…