【设计模式】使用中介者模式优化表单交互

ops/2024/12/23 0:12:57/

我们想象一下机场的指挥塔,如果没有指挥塔的存在,每一架飞机要和方圆 100 公里内的所有飞机通信,才能确定航线以及飞行状况,后果是不可想象的。现实中的情况是,每架飞机都只需要和指挥塔通信。指挥塔作为调停者,知道每一架飞机的飞行状况,所以它可以安排所有飞机的起降时间,及时做出航线调整。

什么是中介者模式

在程序里,也许一个对象会和其他 10 个对象打交道,所以它会保持 10 个对象的引用,并且自己维护与其他对象的交互逻辑。

当程序的规模增大,对象会越来越多,它们之间的关系也越来越复杂,难免会形成网状的交叉引用。

<a class=中介者模式-1.png" />

中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用所以当一个对象发生改变时,只需要通知中介者对象即可。

中介者模式里,对象之间几乎不知道彼此的存在,它们只能通过中介者对象来互相影响对方。

<a class=中介者模式-2.png" />

中介者模式使各个对象之间得以解耦,以中介者和对象之间的一对多关系取代了对象之间的网状多对多关系。

各个对象只需关注自身功能的实现,对象之间的交互关系交给了中介者对象来实现和维护。

示例:要素之间相互影响的商品表单

假设一个商品选择表单有如下功能:

  • 剩余数量受所选颜色和内存影响。
  • 购买数量超过剩余数量时提交按钮置灰。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><form id="form"><div><label for="color">颜色</label><select name="color" id="color"><option value="red">red</option><option value="blue">blue</option><option value="green">green</option></select></div><div><label for="memory">内存</label><select name="memory" id="memory"><option value="red">2G</option><option value="blue">4G</option><option value="green">8G</option></select></div><div><label for="num">购买数量</label><input name="num" type="number" id="num" /></div><div><span>剩余数量:</span><span id="surplus"></span></div><button id="btn">提交</button></form><script>const form = document.getElementById("form");const numInput = form.num;const colorSelect = form.color;const colorMemory = form.memory;const btn = form.btn;const surplus = document.getElementById("surplus");// 根据参数获取剩余数量const commodityMap = {};function getCommodityNum(color, memory) {const key = `${color}-${memory}`;if (!commodityMap[key]) {commodityMap[key] = Math.floor(Math.random() * 10);}return commodityMap[key];}// 更新剩余数量function updateSurplus() {const num = getCommodityNum(colorSelect.value, colorMemory.value);surplus.innerText = num;}// 更新按钮状态function updateBtnDisabledStatus() {if (Number(surplus.innerText) < Number(numInput.value)) {btn.setAttribute("disabled", true);} else {btn.removeAttribute("disabled");}}numInput.oninput = function () {updateBtnDisabledStatus();};colorSelect.onchange = function () {updateSurplus();updateBtnDisabledStatus();};colorMemory.onchange = function () {updateSurplus();updateBtnDisabledStatus();};btn.onclick = function (e) {const params = {num: numInput.value,color: colorSelect.value,memory: colorMemory.value,};console.log(params);e.preventDefault();};updateSurplus();</script></body>
</html>

最终效果如下:

<a class=中介者模式-示例-1.png" />
<a class=中介者模式-示例-2.png" />

这样的实现方式,需要在每一个表单项的 onchange 事件中维护两种事件:更新按钮状态更新剩余数量

这种写法的弊端在于需要牢记每个表单项之间的关联关系,在后续有变更的情况下要在多处进行修改,同时也产生了一些重复代码。

如果后续需求变动,要再加一个 cpu 的选择,那就要将 onchange 事件处理程序再粘贴一份出来:

// ...
cpuSelect.onchange = function () {updateSurplus();updateBtnDisabledStatus();
};
// ...

中介者模式优化

引入一个中介者类来集中定义处理程序。

// ...
class Mediator {static change(target) {updateBtnDisabledStatus();if (target != numInput) {updateSurplusText();}}
}// ...
numInput.oninput = function () {Mediator.change(this);
};colorSelect.onchange = function () {Mediator.change(this);
};colorMemory.onchange = function () {Mediator.change(this);
};
// ...

这样改动之后,不需要再在表单项的 onchange 事件中处理 更新按钮状态更新剩余数量,而是只触发 Mediator.change 方法。

Mediator.change 方法中根据触发事件的对象来区分要执行什么操作。

这时如果要加一个 cpu 选择,可以添加如下代码:

// ...
cpuSelect.onchange = function () {Mediator.change(this);
};
// ...

可以看到这样的改动对于整体而言改动较小,并且 cpuSelect 对象不需要关注它的改动会造成什么影响。

总结

中介者模式的优点是解除了对象之间的紧密耦合关系,在新建对象以及新建对象关系时提供更高的可维护性和可扩展性。

它的缺点在于它将对象之间交互的复杂性转移成了中介者对象的复杂性,使得中介者对象经常是巨大的,中介者对象自身往往就是一个难以维护的对象

是否使用中介者模式取决于对象之间的耦合程度,毕竟我们写程序是为了快速完成项目交付生产,而不是堆砌模式和过度设计。

参考

《JavaScript 设计模式与开发实践》


http://www.ppmy.cn/ops/19080.html

相关文章

java如何使用webService方式调用对接第三方平台

实际使用记录&#xff0c;做个记录&#xff1a; 1、需要对方提供wsdl文件,该文件中有接口的Ip地址&#xff0c;方法名、参数等详细信息&#xff0c; wsdl文档中targetNamespace为命名空间 <xsd:element name"searchBGDMIInfo">标签中name是方法名&#xff1…

HTTP网络协议的请求方法,具体详解(2024-04-26)

1、HTTP 即超文本传输协议&#xff0c;是一种实现客户端和服务器之间通信的响应协议&#xff0c;它是用作客户端和服务器之间的请求 根据 HTTP 标准&#xff0c;HTTP 请求可以使用多种请求方法。 2、方法分类 HTTP1.0 定义了三种请求方法&#xff1a; GET, POST 和 HEAD 方…

FPGA“题目周周练”活动来啦!

Hi&#xff0c;各位编程精英er~ 不知道大家的FPGA学习之旅到达哪一个阶段了呢&#xff1f;又在这个过程中遇到了哪些困惑&#xff1f; 作为一门高度专业化且充满挑战的技术&#xff0c;FPGA的学习是一场不断思考、认知、持续深化的过程。在这个过程中&#xff0c;思维的敏捷和…

【ARMv9 DSU-120 系列 4 -- Utility bus 详细介绍 1】

请阅读【Arm DynamIQ™ Shared Unit-120 专栏 】 文章目录 DSU-120 Utility bus事务类型访问大小事务长度安全状态总结缓存控制(ARCACHEU 或 AWCACHEU)突发类型(ARBURSTU 或 AWBURSTU)锁定信号(ARLOCKU 或 AWLOCKU)Utility bus acceptance capabilities

神经网络与深度学习(三)——卷积神经网络基础

卷积神经网络基础 1.为什么要学习神经网络1.1全连接网络问题1.2深度学习平台简介1.3PyTorch简介1.4简单示例 2.卷积神经网络基础2.1进化史2.2特征提取2.3基本结构 3.学习算法3.1前向传播3.2误差反向传播3.2.1经典BP算法3.2.2卷积NN的BP算法 4.LeNet-5网络4.1网络介绍4.2网络结构…

MySQL基础操作

前言&#x1f440;~ 结束了java数据结构篇章&#xff0c;紧接着来到MySQL的学习喽~ 如果各位对文章的内容感兴趣的话&#xff0c;请点点小赞&#xff0c;关注一手不迷路&#xff0c;如果内容有什么问题的话&#xff0c;欢迎各位评论纠正 &#x1f91e;&#x1f91e;&#x1f9…

【MATLAB源码-第199期】基于MATLAB的深度学习(CNN)数字、模拟调制识别仿真,输出识别率。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 基于深度学习的调制识别系统利用复杂的数学模型和算法来识别和分类从不同来源接收到的无线信号的调制类型。这种技术的应用广泛&#xff0c;特别是在无线通信、电子战、频谱监测和认知无线电等领域中具有重要价值。调制识别系…

Docker之安装部署

本次部署使用openEuler操作系统&#xff1a; 方式一&#xff1a;使用yum安装(openEuler源中有自带的docker18.09.0版本) [rootnode1 ~]# yum install docker -y [rootnode1 ~]# docker version Client: Version: 18.09.0 EulerVersion: 18.09.0.332 API ver…