第四部分:行为型模式 - 中介者模式 (Mediator Pattern)

接下来,我们学习中介者模式。这个模式用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

  • 核心思想:用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介者模式 (Mediator Pattern)

“用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。” (Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.)

想象一个机场的控制塔:

  • 飞机 (Colleague Objects):多架飞机在机场附近飞行,准备降落或起飞。
  • 控制塔 (Mediator):控制塔负责协调所有飞机的行动。飞机不直接相互通信来决定谁先降落、谁使用哪个跑道。它们都只与控制塔通信。
  • 通信 (Interaction):飞机向控制塔报告自己的状态和意图(如请求降落),控制塔根据整体情况向飞机发出指令(如允许降落、指定跑道、等待)。

如果没有控制塔,飞机之间需要复杂的直接通信来避免碰撞和混乱,这将形成一个复杂的网状通信结构。控制塔将这种网状结构简化为星型结构,所有通信都通过中心节点(控制塔)进行。

1. 目的 (Intent)

中介者模式的主要目的:

  1. 减少对象间的直接依赖:将对象间复杂的网状依赖关系转变为星型依赖关系。各个同事对象不再直接相互引用,而是都只引用中介者。
  2. 集中控制交互逻辑:对象间的交互逻辑被封装在中介者对象中,使得交互逻辑更易于理解和维护。
  3. 促进松耦合:同事对象之间松散耦合,它们可以独立地变化和复用。
  4. 提高系统的可扩展性:当需要修改或增加交互行为时,通常只需要修改中介者类,或者增加新的中介者类,而不需要修改大量的同事类。

2. 生活中的例子 (Real-world Analogy)

  • 聊天室 (Chat Room)

    • 用户 (Colleague Objects):聊天室中的多个用户。
    • 聊天室服务器 (Mediator):用户发送消息给服务器,服务器再将消息广播给聊天室中的其他用户(或特定用户)。用户之间不直接点对点发送消息。
  • 联合国安理会 (UN Security Council)

    • 国家 (Colleague Objects):各个成员国。
    • 安理会 (Mediator):国家之间通过安理会这个平台进行沟通、协调和决策,而不是所有国家都两两直接谈判所有事务。
  • GUI应用程序中的对话框 (Dialog Box)

    • 各种控件 (Colleague Objects):如按钮、文本框、列表框、复选框等。
    • 对话框本身 (Mediator):对话框负责协调其内部控件之间的交互。例如,当一个列表框的选择改变时,对话框可能会启用或禁用某个按钮,或者更新某个文本框的内容。控件之间不直接相互操作。

3. 结构 (Structure)

中介者模式通常包含以下角色:

  1. Mediator (中介者接口/抽象类):定义一个接口用于与各个同事对象通信。它通常包含一个方法,供同事对象在自身状态改变时通知中介者。
  2. ConcreteMediator (具体中介者):实现 Mediator 接口。它了解并维护所有的具体同事类,并负责协调它们之间的交互。它封装了同事之间的复杂交互逻辑。
  3. Colleague (同事接口/抽象类):定义一个接口,包含一个指向中介者对象的引用。每个同事类都知道其中介者对象。
  4. ConcreteColleague (具体同事类):实现 Colleague 接口。每个具体同事类只知道自己的行为,当需要与其他同事交互时,它会通知中介者,由中介者去协调。
    在这里插入图片描述
    工作流程
  • 客户端创建具体中介者对象和所有具体同事对象。
  • 客户端将中介者对象设置给每个同事对象,并将每个同事对象注册到中介者中。
  • 当一个同事对象发生变化或需要与其他同事交互时(例如,用户在GUI中点击了一个按钮 ConcreteColleagueA):
    • 该同事对象会调用其 changed() 方法,通知中介者 (ConcreteMediator)。
    • 中介者接收到通知后,根据预定义的交互逻辑,可能会调用其他同事对象的方法(例如,中介者调用 ConcreteColleagueB.someOperationB())。
  • 同事对象之间不直接通信,所有交互都通过中介者进行。

4. 适用场景 (When to Use)

  • 当一组对象以定义良好但复杂的方式进行通信,导致了对象之间存在大量相互依赖和直接引用时(网状结构)。
  • 当一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象时。
  • 当你想自定义一个分布在多个类中的行为,而又不想生成太多子类时。可以将这些行为提取到中介者中。
  • 在GUI设计中,组件之间的复杂交互(例如,一个组件的状态变化影响其他多个组件)是中介者模式的经典应用场景。

5. 优缺点 (Pros and Cons)

优点:

  1. 减少了类间的依赖:将对象之间复杂的网状通信结构简化为星型结构,降低了同事类之间的耦合度。
  2. 集中控制交互:将对象间的交互行为封装在中介者对象中,使得交互逻辑更易于理解、维护和修改。
  3. 符合迪米特法则:同事类只需要与中介者通信,不需要了解其他同事类。
  4. 提高了同事类的可复用性:由于同事类之间的耦合降低,它们更容易被复用。
  5. 提高了系统的灵活性和可扩展性:修改交互行为通常只需要修改中介者,而不需要修改各个同事类。

缺点:

  1. 中介者可能变得庞大复杂:如果系统中同事对象过多,或者交互逻辑过于复杂,中介者类可能会变得非常庞大,承担过多的责任,难以维护(演变成上帝类 God Object)。
  2. 增加了中介者类:引入了额外的中介者类,增加了系统的类数量。

6. 实现方式 (Implementations)

让我们以一个简单的聊天室为例。用户 (User) 是同事,聊天室 (ChatRoom) 是中介者。

同事接口 (ChatUser - Colleague)
// chat_user.go (Colleague interface - can be an abstract struct or interface)
package colleague// Mediator 定义了中介者需要暴露给同事的方法
type Mediator interface {SendMessage(message string, sender User)
}// User 定义了同事需要暴露给中介者的方法,以及同事自身的方法
type User interface {GetName() stringReceiveMessage(message string, senderName string)Send(message string) // 同事通过此方法经由中介者发送消息SetMediator(mediator Mediator)
}
// User.java (Colleague abstract class or interface)
package com.example.colleague;import com.example.mediator.ChatMediator;public abstract class User {protected ChatMediator mediator;protected String name;public User(ChatMediator mediator, String name) {this.mediator = mediator;this.name = name;}public String getName() {return name;}// 同事发送消息,通过中介者public abstract void send(String message);// 同事接收消息public abstract void receive(String message, String senderName);
}
具体同事 (ConcreteUser - ConcreteColleague)
// concrete_user.go
package colleagueimport "fmt"// ConcreteUser 实现了 User 接口
type ConcreteUser struct {name     stringmediator Mediator
}func NewConcreteUser(name string) *ConcreteUser {return &ConcreteUser{name: name}
}func (u *ConcreteUser) SetMediator(mediator Mediator) {u.mediator = mediator
}func (u *ConcreteUser) GetName() string {return u.name
}func (u *ConcreteUser) Send(message string) {fmt.Printf("%s sends: %s\n", u.name, message)if u.mediator != nil {u.mediator.SendMessage(message, u) // 通过中介者发送}
}func (u *ConcreteUser) ReceiveMessage(message string, senderName string) {fmt.Printf("%s receives from %s: %s\n", u.name, senderName, message)
}
// ConcreteUser.java (ConcreteColleague)
package com.example.colleague;import com.example.mediator.ChatMediator;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;public class ConcreteUser extends User {public ConcreteUser(ChatMediator mediator, String name) {super(mediator, name);}@Overridepublic void send(String message) {String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));System.out.println(time + " [" + this.name + "] sends: " + message);mediator.sendMessage(message, this);}@Overridepublic void receive(String message, String senderName) {String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));System.out.println(time + " [" + this.name + "] received from [" + senderName + "]: " + message);}
}
中介者接口 (ChatMediator - Mediator)
// chat_mediator.go (Mediator interface - already defined in colleague/chat_user.go for Go)
// For clarity, we can define it in its own package if preferred.
// package mediator
// type ChatMediator interface {
//    SendMessage(message string, sender colleague.User)
//    AddUser(user colleague.User)
// }
// ChatMediator.java (Mediator interface)
package com.example.mediator;import com.example.colleague.User;public interface ChatMediator {void sendMessage(String message, User sender);void addUser(User user);
}
具体中介者 (ConcreteChatRoom - ConcreteMediator)
// concrete_chat_room.go
package mediator // Assuming this is in a 'mediator' packageimport ("../colleague" // Path to colleague package"fmt"
)// ConcreteChatRoom 实现了 colleague.Mediator 接口
type ConcreteChatRoom struct {users []colleague.User
}func NewConcreteChatRoom() *ConcreteChatRoom {return &ConcreteChatRoom{users: make([]colleague.User, 0),}
}func (cr *ConcreteChatRoom) AddUser(user colleague.User) {fmt.Printf("ChatRoom: %s joined.\n", user.GetName())cr.users = append(cr.users, user)user.SetMediator(cr) // 关键:将中介者设置给同事
}// SendMessage 是 colleague.Mediator 接口的实现
func (cr *ConcreteChatRoom) SendMessage(message string, sender colleague.User) {for _, u := range cr.users {// 不把消息发回给发送者自己if u.GetName() != sender.GetName() {u.ReceiveMessage(message, sender.GetName())}}
}
// ConcreteChatMediator.java (ConcreteMediator)
package com.example.mediator;import com.example.colleague.User;
import java.util.ArrayList;
import java.util.List;public class ConcreteChatMediator implements ChatMediator {private List<User> users;public ConcreteChatMediator() {this.users = new ArrayList<>();}@Overridepublic void addUser(User user) {System.out.println("ChatRoom: " + user.getName() + " joined the chat.");this.users.add(user);// In Java, the mediator is typically passed to User's constructor,// so no need for user.setMediator(this) here if done in constructor.}@Overridepublic void sendMessage(String message, User sender) {for (User user : users) {// Don't send the message back to the senderif (user != sender) {user.receive(message, sender.getName());}}}
}
客户端使用
// main.go (示例用法)
/*
package mainimport ("./colleague""./mediator"
)func main() {chatRoom := mediator.NewConcreteChatRoom()user1 := colleague.NewConcreteUser("Alice")user2 := colleague.NewConcreteUser("Bob")user3 := colleague.NewConcreteUser("Charlie")// 用户加入聊天室,聊天室会自动设置自己为用户的中介者chatRoom.AddUser(user1)chatRoom.AddUser(user2)chatRoom.AddUser(user3)fmt.Println("\n--- Chatting Starts ---")user1.Send("Hi everyone!")// Expected:// Alice sends: Hi everyone!// Bob receives from Alice: Hi everyone!// Charlie receives from Alice: Hi everyone!fmt.Println("\n")user2.Send("Hello Alice!")// Expected:// Bob sends: Hello Alice!// Alice receives from Bob: Hello Alice!// Charlie receives from Bob: Hello Alice!
}
*/
// Main.java (示例用法)
/*
package com.example;import com.example.colleague.ConcreteUser;
import com.example.colleague.User;
import com.example.mediator.ChatMediator;
import com.example.mediator.ConcreteChatMediator;public class Main {public static void main(String[] args) {ChatMediator chatRoom = new ConcreteChatMediator();User user1 = new ConcreteUser(chatRoom, "Alice");User user2 = new ConcreteUser(chatRoom, "Bob");User user3 = new ConcreteUser(chatRoom, "Charlie");chatRoom.addUser(user1);chatRoom.addUser(user2);chatRoom.addUser(user3);System.out.println("\n--- Chatting Starts ---");user1.send("Hi everyone!");// Expected output will show timestamps and formatted messagesSystem.out.println(""); // For spacinguser2.send("Hello Alice! How are you?");System.out.println("");user3.send("I'm good, thanks for asking!");}
}
*/

7. 总结

中介者模式通过引入一个中心协调对象(中介者),将系统中原本复杂的对象间网状交互结构转变为星型结构。这有效地降低了对象之间的耦合度,使得各个对象(同事)可以独立变化,同时也使得交互逻辑集中在中介者中,更易于管理和维护。虽然它可能导致中介者自身变得复杂,但在处理多对多对象交互的场景下,中介者模式提供了一种优雅且有效的解决方案,尤其在GUI开发和需要解耦多个协作组件的系统中非常有用。

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

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

相关文章

Day01_C数据结构

01.数据结构 02.段错误出现的四种场景 02.实现顺序表的头插、尾插、头删、尾删(释放顺序表) main.c #include "seq.h" int main(){ seq_p Screate_seqlist(); inputall(S); insert_head(S); dele…

触觉智能RK3576核心板,工业应用之4K超高清HDMI IN视频输入

在工业自动化、医疗影像、轨道交通、电力调度等行业&#xff0c;对高质量视觉信号的实时捕捉和分析需求日益提高。传统工业相机的低分辨率采集模糊了关键细节&#xff0c;延迟的处理过程导致生产环节无法形成闭环控制&#xff0c;让不同硬件之间的协作障碍重重。 触觉智能RK35…

清新文艺手绘学习教育培训竞标汇报PPT模版分享

简约手绘花朵PPT模版&#xff0c;读书学习教育培训学习总结设计PPT模版&#xff0c;商业竞标企业创业总结汇报演讲报告PPT模版&#xff0c;创意动物卡通PPT汇报模版&#xff0c;学术报告PPT模版 清新文艺手绘学习教育培训竞标汇报PPT模版分享

【搜狗输入法】如何使用自定义标点设置来输出直角引号

【搜狗输入法】如何使用自定义标点设置来输出直角引号 前言&#xff1a; 起因是&#xff0c;我在学习Markdown的语法规范的时候 需要用到直角引号「」 但是键盘没法直接打出来&#xff0c;就想用搜狗输入法的自定义标点 结果发现这功能完全是个鸡肋&#xff0c;没法用 一…

HarmonyOS5 运动健康app(二):健康跑步(附代码)

一、数据模型&#xff1a;构建运动记录的数字骨架 代码通过RunRecord接口定义了跑步数据的核心结构&#xff1a; interface RunRecord {id: string; // 记录唯一标识date: Date; // 跑步日期distance: number; // 距离&#xff08;公里&#xff09;duratio…

29-Oracle 23ai Flashback Log Placement(闪回日志灵活配置)

小伙伴们有没有被各种存储路径满导致的业务崩&#xff0c;半夜起来清理的经历。一不小心 FRA写满了&#xff0c;导致了实例hang住。 OCM考试&#xff0c;时不时就会冒出来这个直接给instance hang&#xff0c;本就卡的环境中脑袋都卡壳、无从下手&#xff0c;一脸懵直接崩。 …

React表单处理:如何获取输入框(input)的值?(受控组件)

系列回顾&#xff1a; 在前面的文章中&#xff0c;我们已经掌握了State、Props、事件处理、列表渲染和条件渲染。我们的应用已经能展示动态内容并响应用户的点击。现在&#xff0c;我们要 tackling 一个非常常见的需求&#xff1a;如何获取用户在表单输入框&#xff08;<inp…

探索现代 Web 开发:从 HTML5 到 Vue.js 的全栈之旅

在当今快速发展的互联网时代&#xff0c;Web 开发已经成为构建数字世界的重要基石。无论是企业级应用、社交媒体平台&#xff0c;还是个人博客和电商平台&#xff0c;Web 技术都在背后默默支撑着这些系统的运行。随着前端技术的不断演进&#xff0c;开发者们已经不再局限于传统…

ElasticSearch聚合查询从15秒到1.2秒的深度优化实践

一、问题背景 在金融风控场景中,我们需要对90天内的交易数据进行多维度聚合分析(按风险等级、地区、金额分段等)。随着数据量增长到日均3000万+记录,原有查询响应时间逐渐恶化至15秒以上,严重影响了业务决策效率。 二、原始架构性能分析 1. 集群拓扑 # 原单节点配置 N…

2025.06.09【读书笔记】|PromptBio:让生信分析更简单的AI平台

文章目录 一、PromptBio 是什么&#xff1f;二、主要功能介绍1. 对话式智能体&#xff0c;像聊天一样做分析2. 自动化工作流&#xff0c;省时省力3. 数据管理一站式搞定4. 机器学习也能一键搞定5. “无代码”到“全代码”&#xff0c;人人都能用 三、适合哪些人用&#xff1f;四…

实战解析:如何用克魔(KeyMob)等工具构建iOS应用稳定性与数据可观测体系

在iOS开发项目逐渐走向复杂化的今天&#xff0c;团队对“可观测性”的要求正不断提升。开发者不仅要知道App是否运行正常&#xff0c;更要明确“为什么异常、在哪里异常、是否可复现”。传统的调试工具往往侧重单一维度&#xff0c;要么是资源监控、要么是日志分析&#xff0c;…

如何轻松实现多源混算报表

报表作为综合业务&#xff0c;数据来源多种多样。传统实现多源混合查询报表要通过 ETL 将数据同库&#xff0c;但这种方式数据时效性太差使用场景受限。通过逻辑数仓能获得较强的数据实时性&#xff0c;但体系又过于沉重&#xff0c;为报表业务搭建逻辑数仓有点得不偿失。需要一…

Docker|简单入门

文章目录 Docker简介Docker和虚拟机的联系和区别基本原理和概念镜像容器仓库 Docker安装配置容器化和Dockerfile实践环节Docker Compose Docker简介 Docker是一个用于构建build、运行run、传送share应用程序的平台&#xff0c;可以把应用程序打包成一个个的集装箱&#xff0c;…

阿里云云原生数据库PolarDB和普通云数据库的区别?

文章目录 前言一、云数据库的演进&#xff1a;从“托管”到“原生”的跨越二、PolarDB的核心创新&#xff1a;重新定义云数据库的能力边界1. 存算分离架构&#xff1a;打破资源绑定的“枷锁”2. 多模引擎与兼容生态&#xff1a;降低应用迁移成本3. 智能化运维&#xff1a;让数据…

SNN学习(4):真实的生物神经学中神经元和人脑结构学习

目录 一、基础知识 1 简单神经元回路中的信号运作 2 高级功能相关的复杂神经元回路 3 细胞体、树突和轴突 3.1 神经元细胞 3.2 非神经元细胞 3.3 神经胶质细胞 3.4 神经细胞的信号传递 3.4.1 动作电位的特性 3.4.2 兴奋和抑制 3.4.3 电传递 二、大脑皮层及视觉系统…

第六天 界面操作及美化(6.1 建立菜单及异步调用)

6.1 建立菜单及异步调用 在程序中&#xff0c;菜单&#xff08;Menu&#xff09;是一种常见的用户界面元素&#xff0c;在程序中起到了组织功能、提高用户体验、提供快捷方式和帮助文档等重要作用。通过合理使用菜单&#xff0c;可以使程序的功能更加清晰、操作更加便捷&#…

论文解析:一文弄懂ResNet(图像识别分类、目标检测)

目录 一、相关资源 二、Motivation 三、技术细节 1.残差学习过程 2.快捷连接类型 (1)Identity Shortcuts&#xff08;恒等捷径&#xff09; (2)Projection Shortcuts&#xff08;投影捷径&#xff09; (3)两种捷径对比 3.深层瓶颈结构Deeper Bottleneck Architectures…

动态规划算法的欢乐密码(二):路径问题

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 一、例题讲解 1.1. 不同路径 题目要求是计算从网格的左上角&#xff08;起点&#xff09;到右下角&#xff08;终点&#xff09;的所有不同路径的数量。机器人每次只能向下或向右移动一步。如下图所示&#xff0…

嵌入式相关开源项目、库、资料------持续更新中

嵌入式相关开源项目、库、资料------持续更新中 学习初期最难找的就是找学习资料了&#xff0c;本贴精心汇总了一些嵌入式相关资源&#xff0c;包括但不限于编程语言、单片机、开源项目、物联网、操作系统、Linux、计算机等资源&#xff0c;并且在不断地更新中&#xff0c;致力…

图像处理与机器学习项目:特征提取、PCA与分类器评估

图像处理与机器学习项目:特征提取、PCA与分类器评估 项目概述 本项目将完成一个完整的图像处理与机器学习流程,包括数据探索、特征提取、主成分分析(PCA)、分类器实现和评估五个关键步骤。我们将使用Python的OpenCV、scikit-learn和scikit-image库来处理图像数据并实现机器…