
文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载导读Mediator中介者模式是行为型设计模式之一它用一个中介对象封装一系列对象之间的交互让对象之间不再直接相互引用从而大幅降低耦合度。本文以《前端精读周刊》设计模式系列 183.精读《设计模式 - Mediator 中介者模式》 为骨架结合本仓库 可视化搭建 系列中组件值联动、联动协议的真实实现为你讲透中介者模式在复杂前端系统中的落地方式——读完你将理解数据即中介的本质掌握用数据层替代 UI 关系网来治理大型项目的方法并学会识别中介者模式的适用边界与滥用风险。一、什么是中介者模式用一个中介封装对象交互Mediator中介者模式属于行为型模式它的经典定义如下意图用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用从而使其耦合松散而且可以独立地改变它们之间的交互。拆解这段意图有三个关键点封装交互对象之间的协同方式被集中收敛到中介者内部而不是散落在各个对象之间。消除显式引用参与协作的对象不再需要认识彼此只需认识中介者即可。可独立改变交互协同规则的变化被隔离在中介者内部对参与对象透明。前端开发中最常见的中介者模式诠释就是数据驱动。接下来我们用一组具体场景说明为什么过程式编程在复杂表单面前会失控而数据驱动可以轻松化解。二、场景引入为什么复杂表单必须走数据中介设想一个真实的业务表单存在以下四层递进的耦合问题按钮点击后表单提交按钮需要调用所有表单项去获取表单值——按钮与所有表单项之间产生了引用关系。表单联动当勾选了城市后才出现满意度Input 框——此时城市勾选按钮需要引用满意度 Input 框。循环引用两个输入框互斥输入了一个另一个就要 Disable——两者互相引用构成环。扩展困难每新增一个表单项都要重新建立所有相关的引用关系。用过程式编程维护这种关系网在大型项目中几乎是不可能的。而数据驱动可以从根本上化解所有表单项都依赖数据、修改数据当 Input 框联动 Checkbox 时Input 并不需要感知 Checkbox 的存在它只需关联数据、修改数据Checkbox 同样只需关联数据、修改数据。这样每个组件的逻辑可以独立完成循环引用问题被天然消解新增表单项时不需要重排关系网。在数据驱动的例子中数据就是中介。所有 UI 之间都不再相互引用而是通过数据这个中介协同工作带来的明显好处是可以支撑复杂项目且易于维护。三、三个经典例子体会中介者的使用场景设计模式的价值在于用起来。以下三个例子覆盖了前端工程、模块系统和组织管理三种视角。3.1 数据驱动让复杂度从几何级增长回归线性正如开篇所说数据驱动是中介者模式最经典的例子。正是引入了数据中介者才让前端项目的业务复杂度可以呈几何倍数递增而代码的逻辑复杂度仅线性递增。原因在于UI 是杂乱且动态的UI 间直接依赖会形成一张难以管理的关系网一旦关系网形成增加一个新元素或修改既有元素都异常困难。中介者模式避开了 UI 间依赖的关系网——通过数据层统一调度、UI 受控响应大大降低了逻辑复杂度。3.2 解决循环依赖第三方中介是唯一出路循环依赖几乎只能利用中介者模式解决。考虑如下两个模块// a.ts import { b } from ./b export const a a// b.ts import { a } from ./a export const b b双方相互引用构成循环依赖。这不仅对模块化构建有影响从逻辑上也讲不通——一定存在递归调用问题。此时引入第三方中介者就不仅仅是一种设计模式思维了a、b模块中原本就有一些两边公用的内容必须被提取出来而统一提出去的地方就是中介者模式里的中介者部分。这也是 ES Module 循环依赖场景下最实用的工程化解法。3.3 企业组织架构树状管理优于网状管理在一个树状企业组织架构中每个非叶子结点都是中介者它负责给子节点分配任务、协调他们的工作。这样一来叶子结点不需要有全局观即可工作——它们只需负责去做自己的事情而不需要关心是如何协同的。以原文档中的组织图为例环境部不需要关心人事部做了什么只要专注做好环境事务即可它们之间的协调由总经理处理这是一种分工协作的体现。而只存在于理论中的网状企业管理模型则是没有中介者的反面案例所有节点都是非叶子结点并相互引用每个人既要做自己的工作又要处理自己与公司里其他几万人的协同几乎是一件不可能完成的事情。所以从设计模式角度来看更倾向于使用树状而不是网状结构来管理复杂协作。四、意图再解释中介者协调而非代劳用一个中介对象来封装一系列的对象交互非常好理解直接看字面意思即可。需要注意的微妙之处是对象交互指对象之间如何协同中介者做的是处理对象间协同的工作而不是替每个对象干活。最后一句可以独立地改变它们之间的交互强调对象间协同方式不是一成不变的。比如一个输入框组件只要实现自己的输入功能就行了而不需要关心如何与外界交互。外界可以通过两种方式复用这个组件将其嵌入表单成为表单项的一部分将其包裹一层符号后缀变成一个专门输入金额的金额输入框。无论外界如何编排协同关系输入框自身的实现都无需改变——协同逻辑全部收敛在中介者数据层 / 编排层一侧。五、结构图与角色划分原文档给出了中介者模式的标准结构包含三个角色角色说明Mediator中介者接口定义一些通信 API。ConcreteMediator具体的中介者继承 Mediator协调各个对象。Colleague同事类比如之前提到的输入框、文本框每个同事之间只要知道中介者即可彼此不需要知道对方的存在。这套角色模型与前端框架的状态管理 组件架构几乎一一对应Mediator 对应状态层的统一通信接口ConcreteMediator 对应具体的 Store / 数据仓库Colleague 对应业务组件。组件不关心其他组件只关心数据。六、代码例子美术与程序的协同原文档给出了一个 TypeScript 例子描述美术与程序两个工种通过中介协同完成产品开发const memberA new Member(美术) const memberB new Member(程序) const picture memberA.draw() // 美术画出图 const product memberB.code(picture) // 程序按照美术画的图做产品在这个例子中完成了程序与美术的协同他们各自不需要知道对方的存在。如果后续又引入产品、测试等工种他们之间不需要做复杂的关联——只需要在中介者里增加对应协同逻辑即可参与协同的同事类本身保持独立与可复用。七、仓库实证可视化搭建框架中的数据即中介本仓库的可视化搭建系列正是中介者模式在真实前端框架中的实践样本可以对照理解数据作为中介的工程实现。7.1 组件值组件与组件之间的唯一中介在可视化搭建框架中每个组件实例都有一个唯一的组件值通过getValue(componentId)与setValue(componentId, value)访问或更新const table { componentName: table, runtimeProps: ({ componentId, setValue }) ({ // 给组件注入 onChange 函数在其触发时更新当前组件实例的组件值 onChange: (value) setValue(componentId, value), }), };组件之间不直接引用而是以组件值为中介协同一个组件只负责读写组件值另一个组件只负责响应组件值的变化。这正是中介者模式中Colleague 只认识 Mediator的落地形态。原文档还回答了为什么不用props.value代替组件值不同组件对 props 的定义各异有的用props.value有的用props.checked只有抽象出统一、与组件元信息解耦的组件值规则才能让任意类型的组件接入同一套联动体系——中介者必须提供中立、统一的通信介质。7.2 值联动声明式定义组件间协同框架通过componentMeta.valueRelates声明式定义联动关系const table { componentName: table, valueRelates: ({ componentId, selector }) { return [ { sourceComponentId: componentId, // 自己为触发源 targetComponentId: selector(({ props }) props.targetComponentId), // 目标组件 ID 为 props.targetComponentId }, ]; }, };这套设计同时满足四个关键诉求恰好对应中介者模式的收益任意组件实例可定义多个联动关系实现多对多联动selector可响应全局状态或 props 变化联动关系可动态更新可以定义自己不在联动链中的纯协调关系组件实例销毁时其联动关系自动失效——协同关系由中介统一管理随生命周期自清理。目标组件通过selector(({ relates }) relates)拿到作用于自己的联动状态并响应渲染const table { componentName: table, runtimeProps: ({ selector }) { // relates 结构[{ sourceComponentId: abc, value: 123 }] const relates selector(({ relates }) relates); return { status: relates.length 0 ? linked : free, }; }, };组件完全不需要知道是谁联动了自己只需要感知联动数据这就是数据中介的直接体现。7.3 联动协议在中介之上构建可拓展的协同规则更进一步定义联动协议展示了如何基于组件值与值联动构建业务可自定义的协同协议——相当于为中介者模式定义了通信 API即 Mediator 接口{ componentName: input, linkage: [{ target: input1, do: { value: {{ $self.value hello }} } }] }target联动目标do联动效果$self描述自己实例可从$self.value拿到自己的组件值从$self.props拿到自己的 props。协议实现的关键一步是把相对关系$self、$deps[0]转换为绝对关系$deps[componentId]再通过valueRelates注册为组件值关联关系——所有组件间的协同都收敛到 valueRelates 这个中介上协议变化后联动状态实时更新。这与原文档中介者处理对象间协同、对象各自独立的思想完全一致。从源码结构看这套联动体系还支持组件间传递A 的组件值同步到 BB 的组件值同步到 C则 AsetValue()后 B、C 会同时更新——正是一个中介协调多个同事类的链式协同示例。八、相关模式辨析中介者 vs 观察者中介者模式常与观察者模式混淆两者都处理对象间通信但关注点不同维度中介者模式Mediator观察者模式Observer关系形态多对多交互被集中到中介者一对多被观察者维护观察者列表通信方向同事类之间经由中介者转发观察者订阅目标目标通知观察者核心职责封装与协调交互逻辑状态变更时通知并更新依赖方解耦程度同事类彼此完全无感知观察者与目标解耦但依赖关系显式在前端实践中两者常常叠加使用数据中介者变化后正是通过观察者机制通知所有依赖方刷新 UI。本仓库的组件值与联动与Observer 观察者模式两篇可以对照阅读加深理解。九、弊端与适用边界中介者模式虽然好但过度使用可能使中介者逻辑非常复杂需要警惕两个问题中介者膨胀我们常说管理者直接管理的人数最好不要超过二十人因为协调本身也非常耗费精力。一个中介者节点如果管理的对象过多可能导致中介者本身难以维护甚至出现 BUG。过度解耦当两个对象本身可以构成依赖关系时使用中介者强行解耦带来的只会是更重的理解负担。设计模式应当服务于真实复杂度而不是为了用模式而引入多余抽象。十、总结当一个系统对象很多、之间关联关系很复杂、交叉引用容易产生混乱时就可能适用中介者模式。它的核心价值体现在降低耦合各对象只需感知数据中介不需要感知彼此独立演化协同规则变化被隔离在中介者内部参与对象可以独立改变契合迪米特法则每个对象只了解最少的内容对大型程序非常有益。从数据驱动到联动协议本仓库的可视化搭建系列为中介者模式提供了完整、可运行的工程样本——理解数据即中介你就掌握了治理大型前端项目复杂度的一把关键钥匙。本系列其他设计模式精读参见 readme.md 的设计模式目录例如创建型模式的抽象工厂、行为型模式的观察者与状态模式可横向对比不同模式的适用场景。赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐如何为asc-comm贡献代码Issue、PR与API文档贡献的3条路径完整清单如何为asc comm贡献代码Issue、PR与API文档贡献的3条路径完整清单 asc comm是CANN推出的昇腾AI处理器通算融合场景开源仓库屏蔽硬件通信高性能计算CANNAscend前端精读周刊前端组件测试策略前端精读周刊前端组件测试策略 引言 在前端开发中组件是构建用户界面的基本单元。随着前端应用的复杂性不断增加确保组件的质量和稳定性变得至关重要。前端组件测试文档技术博客教程前端精读周刊深入理解 JavaScript Pipe Operator|提案前端精读周刊深入理解 JavaScript Pipe Operator| 提案 本文源于前端精读周刊第 228 期围绕 TC39 的 Pipe Oper文档技术博客教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考