设计模式 22 · 三个冷门模式:中介者、访问者、解释器

发布时间:2026/10/6 23:26:52

设计模式 22 · 三个冷门模式:中介者、访问者、解释器 行为型的最后三个模式——中介者(Mediator)、访问者(Visitor)、解释器(Interpreter)——我们放在一篇里集中讲。为什么合并?因为它们是二十三式里最冷门、适用面最窄的三个:中介者容易被滥用成上帝类,访问者结构复杂且限制苛刻,解释器则是公认几乎用不到的一个。把它们放一起,不是敷衍,而是要传递一个和全系列一脉相承的态度:这三个模式,你更需要知道的是它们解决什么问题、以及为什么大多数时候你不该用它们,而不是把它们的模板背下来到处套。所以这一篇的写法和前面不同:每个模式我只讲清三件事——它解决什么问题、核心怎么做、以及为什么适用面这么窄,点到为止,不铺长。读完你能认出它们、知道它们的定位,并在极少数真正需要的场景里想起它们,就够了。目录中介者:让一群对象别再两两纠缠访问者:数据结构稳定,操作却不断加解释器:为一种小语言定义文法三个模式的共同点:适用面都很窄一、中介者:让一群对象别再两两纠缠中介者模式解决什么问题?当一堆对象之间两两直接通信、相互引用时,它们会织成一张乱麻般的网——每个对象都要认识一大堆别的对象,任何一个改动都可能牵连一片。中介者的思路:引入一个中介者对象,让所有对象都只跟中介者通信,而不再彼此直接引用,把网状的多对多关系,变成星型的一对多。一个订单场景的例子:下单页的组件联动。下单页上有一堆相互关联的组件:选了优惠券,总金额要变;改了收货地址,运费要变,运费变了总金额又要变;改了商品数量,金额、运费、优惠资格全要重算……如果每个组件都直接持有并调用其他组件,就是一张可怕的网。用中介者,让每个组件的变化都通知下单页中介者,由中介者统一协调这个变了、该让哪些组件跟着更新:// 中介者:统一协调各组件的联动publicclassOrderPageMediator{privateCouponComponentcoupon;privateAddressComponentaddress;privateAmountComponentamount;// ... 持有各组件// 某个组件变了,通知中介者,由它决定联动谁publicvoidchanged(Componentsource){if(sourcecoupon){amount.recalculate();}if(sourceaddress){amount.recalculate();/* 还有运费等 */}// 协调逻辑集中在这里}}// 组件不再直接调别的组件,只通知中介者publicclassCouponComponent{privateOrderPageMediatormediator;publicvoidselect(){// ... 选优惠券mediator.changed(this);// 只告诉中介者我变了,不管谁联动}}好处很清楚:组件之间彻底解耦了,每个组件只认识中介者,不认识其他组件;所有的联动规则集中在中介者一处,清晰可查;加一个新组件,只需让它接入中介者。这和第 11 篇的外观模式有点像(都是加一个中间层),但意图不同:外观是单向的(简化外部对子系统的调用),中介者是双向的(协调内部一群对等对象的相互通信)。为什么它适用面窄、还容易被滥用?最大的风险是——中介者本身会膨胀成一个上帝类。因为所有协调逻辑都往中介者里塞,组件越多、联动越复杂,中介者就越臃肿,最后变成一个几百上千行、什么都管、谁都不敢碰的巨无霸。你只是把分散在各处的耦合,转移成了高度集中在一个类里的复杂度。所以中介者的适用场景很挑:只有当对象间确实是多对多的复杂交互、且交互逻辑适合集中管理时才用;如果对象关系本来就简单、或只是单向依赖,用它纯属自找麻烦。现实里 GUI 框架的表单联动、聊天室(用户通过服务器中转消息,而非两两直连)是它比较合适的场景。二、访问者:数据结构稳定,操作却不断加访问者模式解决什么问题?当你有一个稳定的数据结构(元素的种类基本固定),但需要不断给它增加新的操作时,访问者能让你在不修改元素类的前提下,新增操作。它的核心手法有点绕:把操作从元素类里抽出来,封装成一个个独立的访问者;元素只提供一个accept(visitor)方法,把自己交给访问者去处理。一个订单场景的例子:对订单集合做多种统计/导出操作。假设订单里有不同类型的元素(实物商品、虚拟商品、服务),你需要对它们做各种操作:算总价、导出 Excel、生成报表、统计重量……而且这些操作会不断新增。如果把每个操作都写进元素类,那每加一个操作,所有元素类都要改。访问者反过来:// 访问者接口:为每种元素类型定义一个 visit 方法publicinterfaceOrderVisitor{voidvisit(PhysicalItemitem);voidvisit(VirtualItemitem);}// 每个操作是一个访问者publicclassPriceVisitorimplementsOrderVisitor{voidvisit(PhysicalItemitem){/* 算实物价 */}voidvisit(VirtualItemitem){/* 算虚拟价 */}}publicclassExportVisitorimplementsOrderVisitor{/* 导出逻辑 */}// 元素只提供 accept,把自己交给访问者publicclassPhysicalItemimplementsItem{publicvoidaccept(OrderVisitorvisitor){visitor.visit(this);}}于是新增一个操作(比如统计重量),只需写一个新的WeightVisitor,所有元素类一个字都不用改。这就是访问者的价值——把操作这个变化维度,从元素类里彻底解放出来。为什么它适用面极窄?因为它有一个致命的倾斜性(和第 4 篇抽象工厂的倾斜性异曲同工):它对增加操作友好,但对增加元素类型极其敌视。新增一个操作 → 加一个访问者,爽;但新增一种元素类型(比如加个ServiceItem)→ 你得回去修改每一个访问者接口和所有实现,加上对新元素的visit方法——牵一发动全身,彻底违反开闭。所以访问者的适用前提非常苛刻:元素种类必须高度稳定(几乎不变),而操作会频繁增加。满足这个前提的场景本就不多(编译器的 AST 处理是经典例子:语法树节点类型固定,但要对它做类型检查、代码生成、优化等很多种操作)。加上它那套accept/visit的双分派结构本身就绕、可读性差,所以业务开发里极少用到。看到它能认出、知道它解决稳定结构 多变操作即可。三、解释器:为一种小语言定义文法解释器模式解决什么问题?当你需要处理一种简单的、自定义的小语言(比如一套规则表达式、一种查询语法)时,解释器提供一种方式:为这个语言定义一套文法,把每条文法规则表示成一个类,然后用这些类的对象组成一棵语法树,通过遍历这棵树来解释执行表达式。一个订单场景的例子:优惠规则表达式。假设运营想灵活配置优惠规则,比如金额100 AND 会员这样的表达式。解释器会把它拆成一棵树:AND是一个节点,左边是金额100(一个表达式),右边是会员(一个表达式),每种表达式是一个实现了interpret()的类,递归地解释求值:publicinterfaceExpression{booleaninterpret(Contextctx);// 解释:在给定上下文下求值}// 与 表达式publicclassAndExpressionimplementsExpression{privateExpressionleft,right;publicbooleaninterpret(Contextctx){returnleft.interpret(ctx)right.interpret(ctx);// 递归解释子表达式}}// 金额大于 表达式、是会员 表达式 ... 各是一个类你会发现,它的结构本质上就是第 10 篇组合模式(树形结构 递归)——解释器可以看作组合模式在’语言解释’这个特定场景的应用。为什么它几乎用不到?三个原因:其一,适用面极其狭窄——只有需要解释一种自定义小语言这一种场景才用得上,而这种需求本就罕见。其二,类爆炸——文法规则稍微一多,就要定义一大堆表达式类,复杂文法下根本维护不了。其三,有更好的替代——真要处理复杂表达式/语言,现实中都用成熟的工具:正则表达式、脚本引擎(如 Groovy、JS 引擎)、规则引擎(如 Drools)、或专门的解析器生成器(ANTLR),它们比手写解释器强大和健壮得多。所以解释器是二十三式里公认最少用的一个。它的价值更多是思想启发(理解语言是怎么被解析执行的),而非实战工具。了解它是什么、知道真要做这事有更好的轮子,就足够了。四、三个模式的共同点:适用面都很窄把这三个模式放一起收个尾,它们其实共享一个特征,也正是我们合并讲的原因:适用场景都非常窄,且都有明显的反噬风险。模式解决什么为什么少用中介者多对多交互 → 集中协调中介者易膨胀成上帝类访问者稳定结构 多变操作加元素类型就牵一发动全身;结构绕解释器解释自定义小语言场景罕见 类爆炸 有更好的轮子这三个模式,恰恰是全系列别过度设计这条主线最好的注脚。它们不是高级“厉害的模式,恰恰相反,它们是最需要克制的模式——因为它们结构复杂、限制苛刻,一旦用错场景,带来的复杂度远超收益。真正成熟的做法,不是我学会了访问者所以要找机会用”,而是我知道访问者的存在,但我清楚我这个场景的元素类型还会变,所以我不用它。知道一个模式什么时候不该用,和知道它怎么用,同等重要——甚至更重要。用一张图把这三个冷门模式的定位和别用它的信号钉在一起:小结。这一篇把行为型里最冷门的三个模式集中讲了:中介者用一个中介者集中协调一群对象的多对多交互,把网状耦合变星型,但要警惕它膨胀成上帝类;访问者把操作从稳定的数据结构里抽出来、以便不改元素就新增操作,但它对新增元素类型极度敌视、结构也绕,业务里极少用;解释器为一种自定义小语言建语法树来解释执行,但场景罕见、易类爆炸、且有正则/脚本引擎/规则引擎等更好的替代,是最少用的一个。它们共同的教训呼应全系列的主线——这些模式最需要的是克制,知道什么时候不该用它们,比会用更重要。下一篇是整个「设计模式拆解」系列的收尾总纲:我们会盘点 JDK/Spring/MyBatis 源码里的模式、集中辨析那些容易混淆的成对模式、再谈一次过度设计,把二十三个模式和七大原则串成一张完整的地图。
延伸阅读

更多相关文章

2026/10/6 14:07:55

先进过程控制(APC)在半导体良率提升中的核心作用与实践指南

1. 项目概述:当良率遇到瓶颈,我们该看向哪里?在晶圆制造的漫长链条里,良率(Yield)是最终决定一切商业成败的“圣杯”。产线上的工程师们每天都在和光刻对准、薄膜厚度、刻蚀均匀性这些具体的工艺参数搏斗&a…

2026/10/6 16:14:56

3分钟上手:B站视频下载神器BiliDownloader全面指南

3分钟上手:B站视频下载神器BiliDownloader全面指南 【免费下载链接】BiliDownloader BiliDownloader是一款界面精简,操作简单且高速下载的b站下载器 项目地址: https://gitcode.com/gh_mirrors/bi/BiliDownloader 还在为无法保存喜欢的B站视频而烦…

2026/9/26 3:55:28

基于SpringBoot的智能物业管理系统设计与实现

1. 项目概述小区物业管理系统是现代社区管理的重要工具,它通过信息化手段整合物业资源、提升服务效率。基于Java和SpringBoot的智能化物业管理系统,能够实现业主信息管理、物业费收缴、报修处理、设备维护等核心功能,同时支持移动端访问&…

2026/10/6 23:24:53

UE5 Niagara粒子系统:死神特效完整制作流程与模块化思维

提到“死神特效”,你脑海里会浮现什么?暗色斗篷扬起的烟尘、往上升腾的幽蓝灵魂、刀刃划过的发光残影、地面散开的黑色法阵。这类效果几乎是暗黑系BOSS战、召唤仪式和死亡结算画面的标配。在UE里把这类效果做出来,今天绕不开的工具就是Niagar…

2026/10/6 23:24:53

用JavaScript优化大模型调用:Token压缩、缓存与模型路由实战

平时写 JavaScript 多半是在折腾页面交互、接口数据,但你可能没想到,它也能跑去给大语言模型“打理财务”。我这里说的不是炒股,而是真正帮大语言模型省钱省电:用 200 行不到的 JavaScript,写了一个能压缩请求、缓存结…

2026/10/6 23:24:53

RTS5411 Type-C USB3.0 HUB 原理图与PCB设计全解析

1. 为什么选择 RTS5411 来做这个 USB3.0 HUB1.1 从一颗芯片说起:RTS5411 的定位与核心优势搞硬件设计的同行应该都有体会,USB HUB 芯片市场长期被几家大厂把持,选型时既要看通道数、协议支持,又要考虑封装、功耗、外围元件数量和整…

2026/10/6 23:24:53

AI Agent 记忆底座实战:从向量库选型到检索策略与更新机制

前阵子帮朋友排查一个客服 Agent 的诡异行为:客户在对话里明确说了三遍“以后公司统一走银行转账,不再用支付宝了”,Agent 当时答应得好好的,下一轮客户问“收款有什么要注意的”,它张口就是“支持支付宝付款&#xff…

2026/10/6 23:24:53

MOS管衬底接法全解析:从原理图到版图的避坑指南

1. 衬底到底该接哪儿:一个被很多人忽略的基础问题做模拟电路或者版图的朋友,尤其是刚入行的,大概率都遇到过这个场景:画原理图的时候,NMOS的衬底引脚随手就接到了地上,PMOS的衬底引脚随手就接到了电源上&am…

2026/10/6 23:19:53

CPU性能优化方法论:从流水线、缓存到伪共享的硬件原理

1. 硬件为什么快:藏在芯片里的几台“流水线工厂”硬件体系结构这门学问,听起来像是学电脑的人才会碰的东西。但你要是经历过线上服务偶发超时、数据库连接被打满、或者同一套代码换台机器性能翻倍却说不清原因,就会意识到:硬件不是…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 0:03:23

MR25H40CDF+STM32F031C6工业级高可靠数据存储方案

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的 PLC 控制柜里、在风电变流器的散热片背面、在矿井监测终端的金属外壳下,你经常能看到一块指甲盖大小的黑色芯片——它既不是 Flash,也不是…

2026/10/6 0:03:23

MRAM+STM32工业断电数据保全实战指南

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的PLC柜里、在野外无人值守的环境监测终端里、在高速运转的包装机控制板上,你经常能看到一块指甲盖大小的黑色芯片,旁边贴着“MR25H40CDF”丝…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑