[UE5 设计模式] 中介者模式Mediator Pattern


参考材料
1. 【UE4 设计模式】策略模式 Strategy Pattern
2. 中介者模式
3. UE开发中的设计模式(二) —— 中介者模式
4. 中介者模式
5. 游戏开发设计模式之中介者模式

1. 概述

1.1 描述

$\cdot$ 中介者模式(Mediator Pattern) 是用来降低多个对象和类之间的通信复杂性, 属于行为型模式.
$\\$ 中介者模式定义了一个中介对象来封装一系列对象之间的交互. 中介者使各对象之间不需要显式地相互引用, 从而使其耦合松散, 且可以独立地改变它们之间的交互.

1.2 套路

$\cdot$ 实现方式:
$\\$ 1) 定义中介者接口: 规定中介者必须实现的接口.
$\\$ 2) 创建具体中介者: 实现中介者接口, 包含协调各同事对象交互的逻辑.
$\\$ 3) 定义同事类: 各个对象不需要显式地相互引用, 而是通过中介者来进行交互.

$\cdot$ 关键代码:
$\\$ 1) 中介者: 封装了对象间的交互逻辑.
$\\$ 2) 同事类: 通过中介者进行通信.

$\cdot$ 结构:
$\\$ 中介者模式包含以下几个主要角色:
$\\$ 1) 中介者(Mediator): 定义了一个接口用于与各个同事对象通信, 并管理各个同事对象之间的关系. 通常包括一个或多个事件处理方法, 用于处理各种交互事件.
$\\$ 2) 具体中介者(Concrete Mediator): 实现了中介者接口, 负责实现各个同事对象之间的通信逻辑. 它会维护一个对各个同事对象的引用, 并协调它们的交互.
$\\$ 3) 同事对象(Colleague): 定义了一个接口, 用于与中介者进行通信. 通常包括一个发送消息的方法, 以及一个接收消息的方法.
$\\$ 4) 具体同事对象(Concrete Colleague): 实现了同事对象接口, 是真正参与到交互中的对象. 它会将自己的消息发送给中介者, 由中介者转发给其他同事对象.

1.3 使用场景

$\cdot$ 当系统中多个类相互耦合, 形成网状结构时.

1.4 优缺点

$\cdot$ 优点:
$\\$ 1) 降低复杂度: 将多个对象间的一对多关系转换为一对一关系.
$\\$ 2) 解耦: 对象之间不再直接引用, 通过中介者进行交互.
$\\$ 3) 符合迪米特原则: 对象只需知道中介者, 不需要知道其他对象.

$\cdot$ 缺点:
$\\$ 1) 中介者复杂性: 中介者可能会变得庞大和复杂, 难以维护.

2. UE5实践

2.1 问题提出

场景中有多个敌人, 有一个UI文本显示场景中的剩余敌人数, 还有一个大门, 当敌人被消灭完后打开. 那么当敌人死亡时, 就需要发布通知, 而UI文本和大门就会收到消息执行相关流程.
$\\$ 看下面大门的蓝图, 可以看到大门需要获取敌人对象数组, 以设置敌人数量和绑定接收通知的事件. 同样UI中也需要相同的操作, 这样带来的问题一个是, 订阅者依赖发布者的信息造成耦合, 另一个是每有一个新的订阅者都需要先获取到所有敌人, 但其实想一想这个信息是共享的, 要是能只获取到一次就完美了.

这时引出中介者模式(Mediator Pattern) 解决这些问题.

2.2 问题解决

我们通过中介者模式来进一步减少对象间的依赖关系.
$\\$ 这里我们再新建BP_GameState作为中介者, 给其添加OnEnemyKilled两个事件分发器.
$\\$ 在敌人蓝图中, 销毁时调用BP_GameState的OnEnemyKilled事件分发器.

那么在UI和大门中就不需要循环每个敌人来绑定事件, 而是通过BP_GameState这个中介者来绑定.

现在还有个问题就是我们在UI和大门中还是需要获取到所有敌人来设置初始的敌人数, 这也不适合当敌人数量会动态增加时的场景.
$\\$ 我们再在BP_GameState中增加一个OnEnemySpawn事件分发器, 在敌人BeginPlay中调用这个事件分发器.

之后在UI和大门中绑定事件, 实现敌人数量的增加即可.

一个优化就是通过新增一个接口, 让BP_GameState来实现接口中的KillEnemy和EnemySpawn函数, 这样我们在每个敌人对象中就不需要转换为BP_GameState了, 这样就算我们以后用了其他的GameState类, 下面的代码仍能够复用.

————————————————
版权声明:本小节内容来源于CSDN博主「hhhcbw」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44491423/article/details/141037518

3. 与其它模式的关系

$\cdot$ 责任链模式, 命令模式, 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:
$\\$ 1) 责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理.
$\\$ 2) 命令在发送者和请求者之间建立单向连接.
$\\$ 3) 中介者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通.
$\\$ 4) 观察者允许接收者动态地订阅或取消接收请求.

$\cdot$ 外观模式和中介者的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作.
$\\$ 1) 外观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能. 子系统本身不会意识到外观的存在. 子系统中的对象可以直接进行交流.
$\\$ 2) 中介者将系统中组件的沟通行为中心化. 各组件只知道中介者对象, 无法直接相互交流.

$\cdot$ 中介者和观察者之间的区别往往很难记住. 在大部分情况下, 你可以使用其中一种模式, 而有时可以同时使用. 让我们来看看如何做到这一点.
$\\$ 中介者的主要目标是消除一系列系统组件之间的相互依赖. 这些组件将依赖于同一个中介者对象. 观察者的目标是在对象之间建立动态的单向连接, 使得部分对象可作为其他对象的附属发挥作用.
$\\$ 有一种流行的中介者模式实现方式依赖于观察者. 中介者对象担当发布者的角色, 其他组件则作为订阅者, 可以订阅中介者的事件或取消订阅. 当中介者以这种方式实现时, 它可能看上去与观察者非常相似.
$\\$ 当你感到疑惑时, 记住可以采用其他方式来实现中介者. 例如, 你可永久性地将所有组件链接到同一个中介者对象. 这种实现方式和观察者并不相同, 但这仍是一种中介者模式.
$\\$ 假设有一个程序, 其所有的组件都变成了发布者, 它们之间可以相互建立动态连接. 这样程序中就没有中心化的中介者对象, 而只有一些分布式的观察者.

4. 如何在大型项目中有效地实现和维护中介者模式?

在大型项目中有效地实现和维护中介者模式, 需要遵循以下步骤和最佳实践:
$\\$ 中介者模式是一种行为型设计模式, 通过引入一个中介对象来封装一系列对象之间的交互, 从而减少对象间的直接依赖关系. 这样可以降低系统的耦合度, 提高系统的灵活性和可维护性.
$\\$ 中介者模式适用于对象之间存在复杂的通信结构, 且这些对象经常需要改变其通信方式的情况. 例如, 在一个应用中需要新增功能或修改现有功能时, 如果采用传统的对象间直接通信方式, 可能会导致大量代码的修改和维护工作. 通过中介者模式, 可以在不改变原有对象的前提下, 轻松地添加新的功能.
$\\$ 抽象中介者类(Mediator) 应该定义一个接口用于各同事角色之间的通信. 这个接口应该足够通用, 能够支持所有具体的中介者类.
$\\$ 具体中介者类(Concrete Mediator) 负责协调各同事角色的行为, 并维持它们之间的通信. 具体中介者类必须依赖于各个同事角色, 并通过统一的接口与抽象中介者进行交互.
$\\$ 同事角色(Colleague) 是那些通过中介者进行通信的对象. 每个同事角色都应该知道中介者类的存在, 并通过它与其他同事角色进行通信. 这样可以避免同事角色之间的直接依赖关系.
$\\$ 通过引入中介者, 将对象间的网状两两交互转变为通过中介者居中传递信息, 从而大大降低了同类对象间的耦合度. 例如, 在一个会议室选择页面初始化时, 可以通过中介者模式来管理请求机构信息和加载HTML页面的异步任务.
$\\$ 虽然中介者模式有诸多优点, 但也可能带来一些问题. 例如, 如果中介者本身变得过于复杂, 可能会增加系统的维护难度. 因此, 在设计中介者时, 应尽量保持其简单和高效.
$\\$ 在某些情况下, 可以使用现有的工具和框架来实现中介者模式. 例如, 在ASP.NET Core项目中, 可以使用MediatR组件来实现基于进程内的数据传递.
$\\$ 在项目开发过程中, 持续关注中介者模式的使用情况, 及时发现并解决可能出现的问题. 例如, 定期检查中介者类是否过于复杂, 是否需要进一步简化或重构.

————————————————
版权声明:本小节内容来源于CSDN博主「数学小师Yq」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2302_80644606/article/details/141462792

5. 在使用中介者模式时, 有哪些最佳实践可以遵循以提高系统的性能和可扩展性?

在使用中介者模式时, 遵循一些最佳实践可以显著提高系统的性能和可扩展性. 以下是一些关键的最佳实践:
$\\$ 1) 定义中介者接口和具体中介者类: 首先, 需要定义一个中介者接口, 该接口包含所有同事类需要调用的方法. 然后, 创建具体的中介者类来实现这个接口, 并在其中封装所有同事类之间的交互逻辑.
$\\$ 2) 降低系统复杂度: 中介者模式通过将系统中的对象之间的交互行为集中到中介者对象中进行处理和调度, 从而降低了系统的复杂度.
$\\$ 3) 解耦对象之间的通信: 中介者模式将对象之间的通信解耦, 这意味着对象之间不需要直接通信, 只需要与中介者对象通信. 这种解耦使代码更易于维护和扩展.
$\\$ 4) 避免中介者类承担过多责任: 在使用中介者模式时, 需要注意中介者类的复杂度, 避免中介者类承担过多的责任. 中介者类应专注于管理对象之间的交互, 而不是处理过多的业务逻辑.
$\\$ 5) 灵活扩展: 中介者模式很容易扩展, 可以轻松地添加或删除对象. 这使得系统在需求变化时能够快速适应.
$\\$ 6) 集中管理交互关系: 通过引入一个中介者对象, 将对象之间的交互关系集中管理, 降低了对象之间的耦合性, 使得系统更加灵活和可扩展.
$\\$ 7) 优化性能: 在实际项目中, 需要熟悉中介者模式的性能考量和最佳实践, 如何优化性能以及何时使用中介者模式.

————————————————
版权声明:本小节内容来源于CSDN博主「数学小师Yq」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2302_80644606/article/details/141462792

6. 中介者模式在处理复杂对象交互时的常见挑战及其解决方案是什么?

中介者模式在处理复杂对象交互时的常见挑战及其解决方案如下:

$\cdot$ 常见挑战:
$\\$ 1) 耦合度增加: 尽管中介者模式旨在减少对象间的直接耦合, 但如果中介者本身过于复杂或职责划分不明确, 可能会导致新的耦合问题.
$\\$ 2) 系统难以维护和扩展: 当系统中对象的数量和交互关系增加时, 中介者可能需要频繁修改以适应新的需求, 这会增加系统的复杂性和维护难度.
$\\$ 3) 性能问题: 如果中介者包含大量对象或复杂的逻辑, 可能会对系统性能产生负面影响.

$\cdot$ 解决方案:
$\\$ 1) 简化中介者的职责: 确保中介者仅负责协调对象间的通信, 并避免承担过多的业务逻辑. 可以通过将部分功能分解到其他组件来实现这一点.
$\\$ 2) 使用接口和抽象类: 通过定义中介者的接口或抽象类, 可以更好地管理和控制不同实现之间的交互, 从而提高系统的灵活性和可维护性.
$\\$ 3) 模块化设计: 将中介者及其相关对象进行模块化设计, 使得每个模块独立且易于测试和维护. 这样可以降低整体系统的复杂性.
$\\$ 4) 引入事件驱动机制: 在某些情况下, 可以结合事件驱动机制(如RxJS中的Observable) 来处理异步数据流和复杂的事件处理逻辑, 从而进一步简化对象间的交互.

————————————————
版权声明:本小节内容来源于CSDN博主「数学小师Yq」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2302_80644606/article/details/141462792

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注