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

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

什么是中介者模式?

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

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

中介者模式-1.png

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

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

中介者模式-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>

最终效果如下:

中介者模式-示例-1.png
中介者模式-示例-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 设计模式与开发实践》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go语言并发实战——日志收集系统(八) go语言操作etcd以及利用watch实现对键值的监控

有关包的安装 我们要实现go语言对第三方包的操作需要我们下载第三方包go.etcd.io&#xff0c;下载命令&#xff1a; go get go.etcd.io/etcd/client/v3 ectd的put与get操作 相关函数说明与示例 我们想实现对etcd进行简单的操作的步骤还是比较简单的&#xff0c;在我上一篇文…

AI+BI第二弹:QuickBI已支持智能搭建智能问数

缘起&#xff1a;一场主题分享 吴恩达&#xff08;Andrew Ng&#xff09;教授&#xff0c;DeepLearning.AI和AI Fund的创始人&#xff0c;在美国红杉资本于2024年3月26日举办的AI Ascent活动中&#xff0c;谈到了人工智能代理工作流程的未来及其潜力&#xff0c;这些工作流程有…

跑腿业务和支付业务的具体实现流程

校园云项目 跑腿业务的具体业务分析 该流程适用于很多接单相关的业务场景&#xff0c;或多或少都可以从中得到启发&#xff1b; 整个流程描述&#xff1a; 任务发布&#xff1a; 用户在平台上发布任务&#xff0c;描述需要完成的任务内容&#xff0c;包括取件地址、送达地址…

typedef 定义函数指针

typdef int(*FUNC_TYPE)(int,int) FUNC_TYPE p NULL; 定义了一个函数指针 函数指针作为函数的参数的用法demon

HarmonyOS开发案例:【音乐播放器】

介绍 使用ArkTS语言实现了一个简易的音乐播放器应用&#xff0c;主要包含以下功能&#xff1a; 播放应用中的音频资源文件&#xff0c;并可进行上一曲、下一曲、播放、暂停、切换播放模式&#xff08;顺序播放、单曲循环、随机播放&#xff09;等操作。结合后台任务管理模块&…

python实现钉钉通讯录导出Excel表

Python工具开源专栏 Py0004 python实现钉钉通讯录导出Excel表 Python工具开源专栏前言目录结构部分演示完整代码已在GitHub上开源 前言 需求来源于公司&#xff0c;需要将钉钉通讯录以Excel表的形式导出到本地&#xff0c;方便定期备份。导出的Excel需要处理钉钉用户兼任多部门…

AppleWatch是真的能够减少我iPhone的使用时长

我应该是比较专情的果粉了&#xff0c;我有一台MacBook Pro、iPad Pro、airpods pro 2和iPhone 15 Pro Max。但我还从来没有用过苹果手表。 然后&#xff0c;我就去买了AppleWatchSeries9蜂窝款&#xff0c;并试用了一周&#xff0c;我想知道它是否能帮助我减少使用iPhone的时间…

Sectigo证书申请流程及价格介绍

Sectigo 是一家全球知名的数字证书颁发机构&#xff08;Certificate Authority, CA&#xff09;&#xff0c;自1998年起就开始提供 SSL 证书服务&#xff0c;是全球最早的 CA 机构之一。 一 Sectigo证书申请流程 1 确定证书类型 根据自身的需求确定证书的类型&#xff0c;一…

源码篇--Nacos服务--中章(5):Nacos客户端启动-实例注册-grpc连接建立

文章目录 前言一、 前奏&#xff1a;二、客户端连接的建立&#xff1a;2.1 NacosNamingService 创建&#xff1a;2.2 NacosNamingService 初始化&#xff1a;2.3 NamingClientProxyDelegate 长连接建立&#xff1a;2.3.1 grpc 代理对象创建&#xff1a;2.3.2 NamingGrpcClientP…

Meta Llama 3本地部署

感谢阅读 环境安装收尾 环境安装 项目文件 下载完后在根目录进入命令终端&#xff08;windows下cmd、linux下终端、conda的话activate&#xff09; 运行 pip install -e .不要控制台&#xff0c;因为还要下载模型。这里挂着是节省时间 模型申请链接 复制如图所示的链接 然后…

每周一算法:多起点最短路

题目描述 有一天&#xff0c;琪琪想乘坐公交车去拜访她的一位朋友。由于琪琪非常容易晕车&#xff0c;所以她想尽快到达朋友家。 现在给定你一张城市交通路线图&#xff0c;上面包含城市的公交站台以及公交线路的具体分布。 已知城市中共包含 n n n个车站&#xff08;编号 …

Adobe Firefly Image 3:创新步伐与挑战并存的AI图像生成技术升级

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

编写你的第一个java 程序

1.安装 jdk 网址&#xff1a; Java Downloads | Oracle 一般我们安装jdk 17 就行了 自己练习 自己学习 真正的开发中我们使用jdk 8 这个是最适合开发java 应用程序的 当然你也可以选择你的 系统 来安装这个java 在文件资源管理器打开JDK的安装目录的bin目录&#xff0c;会发…

VSCode通过跳板机免密连接远程服务器的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Android Monkey工具介绍与使用

过于爽快的承认失败&#xff0c;就可能发觉不了曾经与正确非常接近。大家好&#xff0c;依旧是在翻看旧文档的时候&#xff0c;发现一篇关于Monkey的介绍和使用&#xff0c;Monkey这款工具在软件测试中主要用于进行压力测试和稳定性测试。它可以模拟大量随机的用户操作&#xf…

618买什么最划算?618买什么东西便宜?必备数码好物清单分享

​只不&#xff0c;马上又到了618购物节咯&#xff0c;数码产品的优惠力度尤为显著&#xff0c;是购买数码产品的绝佳时机。接下来&#xff0c;我将为大家分享几款性价比超高的数码产品&#xff0c;相信总有一款能吸引你的目光。 一、南卡OE MIX开放式蓝牙耳机 在618购物狂欢节…

javaScript中的闭包

什么是闭包 在理解 JavaScript 中的闭包前先了解以下两个知识点&#xff1a; JavaScript 中的作用域和作用域链JavaScript 中的垃圾回收 简单回顾一下这两个知识点&#xff1a; 1. JavaScript 中的作用域和作用域链 作用域就是一个独立的地盘&#xff0c;让变量不会外泄、…

tomcat 配置支持 ssl 附效果图

1、修改tomcat配置文件server.xml: vim ./conf/server.xml 把配置文件&#xff1a; <Connector port"8088" Server" " protocol"HTTP/1.1"connectionTimeout"20000"redirectPort"8443" URIEncoding"UTF-8" …

C++ | Leetcode C++题解之第46题全排列

题目&#xff1a; 题解&#xff1a; class Solution { public:void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len){// 所有数都填完了if (first len) {res.emplace_back(output);return;}for (int i first; i &…

逆数对(树状数组的方法)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例&#xff1a; 输入 5 4 5 1 3 2 输出 7 思路&#xff1a; 根据题意&#xff0c;求逆序对总数。 逆序对含义&#xff1a;如果数组中的两个不同位置&#xff0c;前面的数字比后面的数字严格大&…