设计模式之中介模式与解释器模式详解和应用
目录
- 1 中介模式详解
- 1.1 中介模式的定义
- 1.1.1 中介者模式在生活场景中应用
- 1.1.2 中介者模式的使用场景
- 1.2 中介模式的通用实现
- 1.2.1 类图设计
- 1.2.2 代码实现
- 1.3 中介模式应用案例之聊天室
- 1.3.1 类图设计
- 1.3.2 代码实现
- 1.4 中介者模式在源码中应用
- 1.4.1 jdk中Timer类
- 1.5 中介者模式使用总结
- 1.5.1 优缺点总结
- 1.5.2 与其他模式的关系总结
- 2 解释器模式详解
- 2.1 解释器模式的定义
- 2.1.1 解释器模式在生活场景中应用
- 2.1.2 解释器模式的适用场景
- 2.2 解释器模式的通用实现
- 2.2.1 类图设计
- 2.2.2 代码实现
- 2.3 解释器模式实现案例之计算器功能
- 2.3.1 类图设计
- 2.3.2 代码实现
- 2.3.3 解释器模式在源码中应用
- 2.4.4 spring中ExpressionParser
- 2.5 解释器模式的使用总结
- 2.5.1 优缺点总结
1 中介模式详解
1.1 中介模式的定义
定义:
中介者模式【Mediator Pattern】,又称调解者模式或调停者模式。用一个中介对象封装一系列的对象
交互,中介者使用各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
核心:
通过中介者解耦系统各层次对象的直接耦合,层次对象的对外依赖通信统统交由中介者转发。
属于行为型模式。
1.1.1 中介者模式在生活场景中应用
1.人际交往圈,通讯网络。如果需要自己逐个去建立关系会很困难且复杂,如果加入中介者,来维护这个网络。
中介者就很方便了。
2.朋友圈。
3.数据整合中心
4.rpc通信
1.1.2 中介者模式的使用场景
1.系统中对象之间存在复杂的引用关系,产生相互依赖关系结构混乱且难以理解。
2.交互的公共行为,如果需要改变行为则可以增加新的中介者类。
1.2 中介模式的通用实现
1.2.1 类图设计

1.2.2 代码实现
1.3 中介模式应用案例之聊天室
很多人进入一个聊天室,或聊天群里,群或聊天室,都充当中介者的角色。
1.3.1 类图设计

说明:
这里通过用户名,表示不同用户。
1.3.2 代码实现
1.中介者【聊天室】
package com.oldlu.mediator.demo.chatroom;/*** @ClassName ChatRoom* @Description 聊天室* @Author oldlu* @Version 1.0*/
public class ChatRoom {public void showMsg(User user,String message){System.out.println("【"+user.getName() +"】,传达信息:"+message);}
}
它就只是一个传话者,把信息发布者的内容,传达出来。
2.用户类【聊天者】
package com.oldlu.mediator.demo.chatroom;/*** @ClassName User* @Description 用户类* @Author oldlu* @Version 1.0*/
public class User {private ChatRoom chatRoom;private String name;public String getName() {return name;}public User(String name,ChatRoom chatRoom) {this.chatRoom = chatRoom;this.name = name;}public void sendMessage(String msg){//用户传达信息,通过聊天室this.chatRoom.showMsg(this, msg);}
}
3.测试类
package com.oldlu.mediator.demo.chatroom;/*** @ClassName Test* @Description 测试类* @Author oldlu* @Version 1.0*/
public class Test {public static void main(String[] args) {ChatRoom chatRoom = new ChatRoom();User tom = new User("Tom",chatRoom);User jerry = new User("Jerry",chatRoom);tom.sendMessage("大家好,我是"+tom.getName()+"欢迎大家,进入聊天室");jerry.sendMessage("大家好,我是"+jerry.getName()+"欢迎大家,进入聊天室");}
}
测试结果:

1.4 中介者模式在源码中应用
1.4.1 jdk中Timer类
private void sched(TimerTask task, long time, long period) {if (time < 0)throw new IllegalArgumentException("Illegal execution time.");// Constrain value of period sufficiently to prevent numeric// overflow while still being effectively infinitely large.if (Math.abs(period) > (Long.MAX_VALUE >> 1))period >>= 1;synchronized(queue) {if (!thread.newTasksMayBeScheduled)throw new IllegalStateException("Timer already cancelled.");synchronized(task.lock) {if (task.state != TimerTask.VIRGIN)throw new IllegalStateException("Task already scheduled or cancelled");task.nextExecutionTime = time;task.period = period;task.state = TimerTask.SCHEDULED;}queue.add(task);if (queue.getMin() == task)queue.notify();}}
Timer类中有多个schedule重载方法,内部都调用sched方法,Timer本身是没有做什么事的。
它只是调用Quequ队列add,把TimerTask添加进去。所以,Timer充当中介者的角色。
1.5 中介者模式使用总结
1.5.1 优缺点总结
优点:
1.减少类间依赖,将多对多依赖关系,转化成一对多的关系,降低了类间的耦合性。
2.类间各司其职,符合迪米特法则。
缺点:
中介者模式,将原本多个对象直接的相互依赖关系,变成依赖中介者和多个同事类的依赖关系。
当同事类越来越多时,中介者责任越来越大,且复杂难以维护。
1.5.2 与其他模式的关系总结
中介者模式和代理模式的区别:
1.都存在中间角色。
2.代理模式,着重在代理【增强功能】
3.中介者模式,只他帮你联系上了,就不管了,后面的事情全权由自己完成。【只管搭桥】
2 解释器模式详解
2.1 解释器模式的定义
定义:
解释器模式【Interpretor Pattern】给定一个语言,定义它的文法的一种表示,并定义解释器。
这个解释器使用该表示来解释语言中的句子。
特征:
为了解释一种语言,而为语言创建的解释器。
属于行为型模式。
2.1.1 解释器模式在生活场景中应用
1.音乐简谱
2.摩斯密码
2.1.2 解释器模式的适用场景
1.一些重复出现的问题可以一种简单的语言来进行表达
2.一个简单的语法 需要解释的场景
2.2 解释器模式的通用实现
2.2.1 类图设计
2.2.2 代码实现
2.3 解释器模式实现案例之计算器功能
2.3.1 类图设计

2.3.2 代码实现
1.计算器功能
package com.oldlu.interpreter.demo.calculate;import java.util.Stack;
/*** @ClassName Calculator* @Description 计算器* @Author oldlu* @Version 1.0*/
public class Calculator {private Stack<IArithmeticInterpreter> stack = new Stack<>();public Calculator(String expression) {parse(expression);}private void parse(String expression) {//解析 10 + 20 表达式String[] eles = expression.split(" ");IArithmeticInterpreter left,right;for(int i= 0;i < eles.length;i++){String operator = eles[i];if(OperateUtil.ifOperator(operator)){left =this.stack.pop();right = new NumInterpreter(Integer.valueOf(eles[++i]));System.out.println("出栈:"+left.interpret()+"和"+right.interpret());this.stack.push(OperateUtil.getInterpreter(left,right,operator));System.out.println("应用运算符:"+operator);}else {NumInterpreter numInterpreter = new NumInterpreter(Integer.valueOf(eles[i]));this.stack.push(numInterpreter);System.out.println("入栈:"+numInterpreter.interpret());}}}public int calculate(){return this.stack.pop().interpret();}
}
2.解释器顶层接口
package com.oldlu.interpreter.demo.calculate;/*** @ClassName IArithmeticInterpreter* @Description 顶层解释器接口* @Author oldlu* @Version 1.0*/
public interface IArithmeticInterpreter {int interpret();
}
3.数值解释器实现
package com.oldlu.interpreter.demo.calculate;/*** @ClassName NumInterpreter* @Description 具体解释器实现类,终结表达式* @Author oldlu* @Date 2020/6/23 14:24* @Version 1.0*/
public class NumInterpreter implements IArithmeticInterpreter {private int value;public NumInterpreter(int value) {this.value = value;}@Overridepublic int interpret() {return this.value;}
}
4.两个操作数解释器抽象类
package com.oldlu.interpreter.demo.calculate;/*** @ClassName Interpreter* @Description 操作数解释器实现* @Author oldlu* @Version 1.0*/
public abstract class Interpreter implements IArithmeticInterpreter {protected IArithmeticInterpreter left;protected IArithmeticInterpreter right;public Interpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {this.left = left;this.right = right;}//这里是一个空方法,具体逻辑在子类实现@Overridepublic int interpret() {return 0;}
}
5.四则运算解释器实现
package com.oldlu.interpreter.demo.calculate;/*** @ClassName AddInterpreter* @Description +运算符解释器* @Author oldlu* @Version 1.0*/
public class AddInterpreter extends Interpreter{public AddInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {super(left, right);}@Overridepublic int interpret() {return this.left.interpret() + this.right.interpret();}
}
package com.oldlu.interpreter.demo.calculate;/*** @ClassName SubInterpreter* @Description 减法解释器* @Author oldlu* @Version 1.0*/
public class SubInterpreter extends Interpreter{public SubInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {super(left, right);}@Overridepublic int interpret() {return this.left.interpret() - this.right.interpret();}
}
package com.oldlu.interpreter.demo.calculate;/*** @ClassName MultiInterpreter* @Description 乘法解释器* @Author oldlu* @Version 1.0*/
public class MultiInterpreter extends Interpreter {public MultiInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {super(left, right);}@Overridepublic int interpret() {return this.left.interpret() * this.right.interpret();}
}
package com.oldlu.interpreter.demo.calculate;/*** @ClassName DivInterpreter* @Description 除法解释器* @Author oldlu* @Version 1.0*/
public class DivInterpreter extends Interpreter{public DivInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {super(left, right);}@Overridepublic int interpret() {return this.left.interpret() / this.right.interpret();}
}
6.操作符工具类
package com.oldlu.interpreter.demo.calculate;/*** @ClassName OperateUtil* @Description 运算符解析工具类* @Author oldlu* @Version 1.0*/
public class OperateUtil {public static boolean ifOperator(String symbol){return (symbol.equals("+") || symbol.equals("-") || symbol.equals("*") || symbol.equals("/"));}public static Interpreter getInterpreter(IArithmeticInterpreter left,IArithmeticInterpreter right,String symbol){if(symbol.equals("+")){return new AddInterpreter(left,right);}else if(symbol.equals("-")){return new SubInterpreter(left,right);}else if(symbol.equals("*")){return new MultiInterpreter(left,right);}else if(symbol.equals("/")){return new DivInterpreter(left,right);}return null;}
}
7.测试类
package com.oldlu.interpreter.demo.calculate;/*** @ClassName Test* @Description 测试类* @Author oldlu* @Version 1.0*/
public class Test {public static void main(String[] args) {System.out.println("result:"+new Calculator("10 + 30").calculate());System.out.println("---------------------------");System.out.println("result:"+new Calculator("10 * 30").calculate());System.out.println("---------------------------");System.out.println("result:"+new Calculator("10 - 30 + 11").calculate());System.out.println("---------------------------");System.out.println("result:"+new Calculator("10 / 30 + 10 * 4 - 15").calculate());}
}
测试结果:

其实,spring-expression模块中已经提供了四则运算的功能。
测试类如下:
package com.oldlu.interpreter.demo.calculate;import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;/*** @ClassName SpringTest* @Description spring表达式包测试* @Author oldlu* @Version 1.0*/
public class SpringTest {public static void main(String[] args) {ExpressionParser parser = new SpelExpressionParser();Expression expression = parser.parseExpression("10 / 30 + 10 * 4 - 15");Integer result = (Integer) expression.getValue();System.out.println("计算结果:"+result);//25}
}
注意:这个代码是有问题的?
后面会进行分析并解决。如:10 * 30 + 10 * 4 - 15,表达式存在优先级问题,默认按从左到右的顺序执行。
添加依赖:
<dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.0.2.RELEASE</version></dependency>
2.3.3 解释器模式在源码中应用
jdk下Pattern类
从构造器出发:
private Pattern(String p, int f) {pattern = p; //存储表达式flags = f;// to use UNICODE_CASE if UNICODE_CHARACTER_CLASS presentif ((flags & UNICODE_CHARACTER_CLASS) != 0)flags |= UNICODE_CASE;// Reset group index countcapturingGroupCount = 1;localCount = 0;if (pattern.length() > 0) {compile(); //进行编译} else {root = new Start(lastAccept);matchRoot = lastAccept;}}
2.4.4 spring中ExpressionParser

它是一个接口,下面查看实现类SpelExpressionParser:
protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException {return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
}
2.5 解释器模式的使用总结
2.5.1 优缺点总结
优点:
1.扩展性强:在解释器模式中,由于语法是由很多类表示,当语法规则更改时,只需要修改相应的非终结符表达式即可。
若扩展语法时,只需添加相应的非终结符类即可。
2.增加了新的解释表达式的方式。
3.易于实现文法:解释器模式对应的文法应当比较简单易于实现的,过于复杂的语法不适合使用解释器模式。
缺点:
1.语法规则比较复杂时,会引起类大量增加。
2.执行效率比较低。
相关文章:
设计模式之中介模式与解释器模式详解和应用
目录1 中介模式详解1.1 中介模式的定义1.1.1 中介者模式在生活场景中应用1.1.2 中介者模式的使用场景1.2 中介模式的通用实现1.2.1 类图设计1.2.2 代码实现1.3 中介模式应用案例之聊天室1.3.1 类图设计1.3.2 代码实现1.4 中介者模式在源码中应用1.4.1 jdk中Timer类1.5 中介者模…...
2023年全国最新交安安全员精选真题及答案1
百分百题库提供交安安全员考试试题、交安安全员考试预测题、交安安全员考试真题、交安安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 一、单选题: 1.对施工组织设计中的安全技术措施或者专项施工方案是否…...
chrome插件开发备忘录
文章目录前言要点通信方式注意事项参考前言 第一次接触到插件开发,起因是我用了十多年的一键上传需要升级了。之前的版本来自于有道云,是通过在书签栏加一个书签,然后将上传功能注入到当前页面的方式来实现的。一直也用得挺好,挺…...
实例1:控制树莓派板载LED灯闪烁
实例1:控制树莓派板载LED灯闪烁 实验目的 通过背景知识学习,了解四足机器人mini pupper搭载的微型控制计算机:树莓派。通过对树莓派板载LED灯的状态读写控制,熟悉树莓派本身的操作及Linux中文件的读写。掌握常见函数time.sleep(…...
华为OD机试题 - 最大排列(JavaScript)
最近更新的博客 华为OD机试题 - 字符串加密(JavaScript) 华为OD机试题 - 字母消消乐(JavaScript) 华为OD机试题 - 字母计数(JavaScript) 华为OD机试题 - 整数分解(JavaScript) 华为OD机试题 - 单词反转(JavaScript) 使用说明 参加华为od机试,一定要注意不要完全背…...
手动创建 vue2 ssr 开发环境
本文和个人博客同步发表 更多优质文章查看个人博客 前言 手动搭建 vue ssr 一直是一些前端开发者的噩梦,因为其中牵扯到很多依赖包之间的配置以及webpack在node中的使用。就拿webpack配置来说,很多前端开发者还是喜欢用webpack-cli脚手架搭建项目。导致…...
RHCE-操作系统刻录工具
Windows 1.准备材料。 一个可用的windows操作系统(下载的时候用迅雷比较快) MSDN, 我告诉你 - 做一个安静的工具站 大于等于8G的U盘 想要安装的系统光盘镜像 U盘烧录工具(软碟通) UltraISO软碟通中文官方网站 - 光盘映像文件制作/编辑/转换工具 …...
PHP面向对象01:面向对象基础
PHP面向对象01:面向对象基础一、关键字说明二、技术实现1. 定义类2. 类成员三、 访问修饰限定符1. public2. protected3. private4. 空修饰限定符四、类内部对象五、构造和析构1. 构造方法2. 析构方法六、范围解析操作符1. 访问类常量2. 静态成员3. self关键字七、类…...
《爆肝整理》保姆级系列教程python接口自动化(十八)--重定向(Location)(详解)
简介 在实际工作中,有些接口请求完以后会重定向到别的url,而你却需要重定向前的url。URL主要是针对虚拟空间而言,因为不是自己独立管理的服务器,所以无法正常进行常规的操作。但是自己又不希望通过主域名的二级目录进行访问&…...
MySQL的索引、视图
什么是索引模式(schema)中的一个数据库对象 在数据库中用来加速对表的查询 通过使用快速路径访问方法快速定位数据,减少了磁盘的I/O 与表独立存放,但不能独立存在,必须属于某个表 由数据库自动维护,表被删除时,该表上的索引自动被…...
【JavaWeb】网络层协议——IP协议
目录 IP协议结构 IP地址管理 特殊IP 解决IP地址不够用 动态分配IP地址 NAT网络地址转换 IPV6 IP协议结构 版本:就是IP协议的版本号。目前只有 4 和 6。这里介绍的是IPV4 首部长度:单位是4字节。于TCP首部长度完全一致,也是可变的&…...
【Python学习笔记】41.Python3 多线程
前言 本章介绍Python的多线程。 Python3 多线程 多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理。用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理…...
Windows 版本ffmpeg编译概述
在使用ffmpeg过程当中,ffmpeg在Linux(包括mac,android)编译非常容易,直接configure,make即可,Android需要交叉编译,在windows就比较麻烦,庆幸的是ffmpeg官方提供已编译好Windows版本的二进制库(http://ffmpeg.org/download.html#b…...
NETCore下CI/CD之自动化测试 (详解篇)
NETCore下CI/CD之自动化测试 (详解篇) 目录:导读 前言 安装JDK 安装 Tomcat 首先,我们需要指定 Tomcat.PID 进程文件,进入 /usr/local/tomcat/bin,编辑文件 增加 tomcat 账户并赋予权限 防止Jeknins…...
Hoeffding不等式剪枝方法
在基于物品的协通过滤算法中,当用户历史行为数据有很多时,对计算会有很大挑战,对此可以使用剪枝对数据进行化简来达到减少计算量。 不是每个物品对都需要进行增量计算。对于两个物品的相似度,每次更新都能够得到一个新的相…...
【算法】数组中的重复数字问题
数组中的重复数据 数组中重复的数字 错误的集合 以第三题,错误的集合为例 对于这样的问题,有很简单的解决方式,先遍历一次数组,用一个哈希表记录每个数字出现的次数,然后遍历一次 [1…N],看看那个元素重…...
数值方法笔记2:解决非线性方程
1. 不动点定理及其条件验证2. 收敛阶、收敛检测与收敛加速2.1 如何估计不动点迭代的收敛阶xk1g(xk){x}_{{k}1}{g}\left({x}_{{k}}\right)xk1g(xk)2.2 给定精度的情况下,如何预测不动点迭代需要迭代的次数2.3 如何加快收敛的速度2.4 停止不定点迭代的条件2.5 不动…...
基于SpringBoot的在线文档管理系统
文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…...
软件体系结构(期末复习)
文章目录软件体系结构软件体系结构概论软件体系结构建模软件体系结构风格统一建模语言基于体系结构的软件开发软件体系结构 软件体系结构概论 软件危机是指计算机软件的开发和维护过程中遇到的一系列严重问题。 软件危机的表现: 软件危机的原因: 软件工程的基本要素…...
[vue3] pinia的基本使用
使用Pinia npm install piniastore文件里index.js import { createPinia } from piniaconst pinia createPinia()export default piniamain.js导入并引用 import { createApp } from vue import App from ./App.vue import pinia from ./storescreateApp(App).use(pinia).m…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
