设计模式之禅【解释器模式】
2019-04-13 21:14 发布
生成海报
真刀实枪之解释器模式
从模型公式说起
需求,输入一个模型公式,然后输入模型中的参数,运算出结果
设计要求
公式可以运行时编辑
高扩展性
效率可以暂不考虑
分析下这个需求,还是比较简单的,就是有一套模板,然后填入参数,计算出相应的结果
这个需求中的角 {MOD}主要有两个
运算元素【终结符号:这个元素除了赋值以外,不需要做其他的任何处理】
运算法则【非终结符号:编写算法进行处理】
运算元素与运算符号的异同点
共同点:都要被解析
不同点:运算元素有相同的功能,可以归为一类;运算符要被分别处理,用不同类来处理
那就从加减模型开始这次的设计之旅吧
加减模型简单类图
看上面这个类图,有什么问题没有?对,运算有先后顺序,加减法可能用不到,但是考虑到扩展性后,乘除加上后就不一样了。好了,问题有了,那么开始解决问题吧!
总的思路有了,现在把细节完善一下吧
设计基本完成,现在动手实现具体的逻辑吧
代码
Expression
package com.peng.js;
import java.util.HashMap;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public abstract class Expression {
public abstract int interpreter(HashMap var);
}
VarExpression
package com.peng.js;
import java.util.HashMap;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public class VarExpression extends Expression {
private String key;
public VarExpression(String key) {
super();
this.key = key;
}
// 从map中取值
@Override
public int interpreter(HashMap var) {
return var.get(key);
}
}
SymbolExpression
package com.peng.js;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public abstract class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
super();
this.left = left;
this.right = right;
}
}
AddExpression
package com.peng.js;
import java.util.HashMap;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
// 把左右两边相加
@Override
public int interpreter(HashMap var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
SubExpression
package com.peng.js;
import java.util.HashMap;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
// 把左右两边相加
@Override
public int interpreter(HashMap var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
Calculator
package com.peng.js;
import java.util.HashMap;
import java.util.Stack;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public class Calculator {
// 定义表达式
private Expression expression;
// 构造函数传参,并解析
public Calculator(String expStr) {
super();
// 定义一个栈,安排运算的先后顺序
Stack stack = new Stack();
// 表达式拆分为字符数组
char[] charArray = expStr.toCharArray();
// 运算
Expression left = null;
Expression right = null;
for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '+': {// 加法
// 加法结果放到栈中
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new AddExpression(left, right));
break;
}
case '-': {// 减法
// 减法结果放到栈中
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
}
default: {
// 公式中的变量
stack.push(new VarExpression(String.valueOf(charArray[i])));
}
}
}
this.expression = stack.pop();
}
// 开始运算
public int run(HashMap var) {
return this.expression.interpreter(var);
}
}
Client
package com.peng.js;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public class Client {
public static void main(String[] args) throws IOException {
String expStr = getExpStr();
// 赋值
HashMap var = getValue(expStr);
Calculator cal = new Calculator(expStr);
// 运算结果
System.out.println("表达式:" + expStr + "的值=" + cal.run(var));
}
private static HashMap getValue(String expStr)
throws IOException {
HashMap map = new HashMap();
// 解析有几个参数要传递
for (char ch : expStr.toCharArray()) {
if (ch != '+' && ch != '-') {
// 解决重复参数的问题
if (!map.containsKey(String.valueOf(ch))) {
System.out.println("请输入" + String.valueOf(ch) + "参数的值:");
String in = new BufferedReader(new InputStreamReader(
System.in)).readLine();
map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
// 获得表达式
private static String getExpStr() throws IOException {
System.out.println("请输入表达式:");
return new BufferedReader(new InputStreamReader(System.in)).readLine();
}
}
执行结果
请输入表达式:
a+b+c
请输入a参数的值:
1
请输入b参数的值:
2
请输入c参数的值:
3
表达式:a+b+c的值=6
解释器模式的定义
Interpreter Pattern
Given a language,define a representation for its grammer along with an interpret uses the representation to interpretwntences in the language.(给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子) 解释器模式的通用类图
解释
AbstractExpression:抽象解释器
TerminalExpression:终结符表达式【eg:参数】
NonterminalExpression:非终结符表达式【eg:符号】
Context:环境角 {MOD} 通用代码
AbstractExpression
package js2;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public abstract class Expression {
// 解析任务
public abstract Object interpreter(Context ctx);
}
TerminalExpression
package js2;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description 终结符表达式
*/
public class TerminalExpression extends Expression {
// 通常终结符表达式只有一个,但是有多个对象
@Override
public Object interpreter(Context ctx) {
return null;
}
}
NonterminalExpression
package js2;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description 非终结符表达式
*/
public class NonterminalExpression extends Expression {
// 每个非终结符表达式都会对其他表达式产生依赖
public NonterminalExpression(Expression... expression) {
super();
}
@Override
public Object interpreter(Context ctx) {
// 进行文法处理
return null;
}
}
Context
package js2;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public class Context {
}
Client
package js2;
import java.util.Stack;
/**
* @author kungfu~peng
* @data 2017年12月3日
* @description
*/
public class Client {
public static void main(String[] args) {
Context context = new Context();
// 通常定义一个语法容器,容纳一个具体的表达式--ListArray,LinkedList,Stack等类型
Stack stack = null;
for (;;) {
// 进行语法树判断,并产生递归调用
}
Expression exp = stack.pop();
// 具体元素进入场景
exp.interpreter(context);
}
}
解释器模式的应用
解释器模式的优点
语法分析工具
扩展性好
解释器模式的缺点
引起类的膨胀
采用递归--调用复杂
效率问题【循环和递归引起的】 解释器模式的使用场景
重复发生的问题【服务器日志的处理】
一个简单语法需要解释的场景【简单:减少循环和递归(SQL语法分析)】 解释器模式使用的注意事项
尽量不要在重要的模块使用--维护是个大问题
可以代替解释器模式的技术:shell,JRuby,Groovy等脚本语言 最佳实践
实际开发中使用的非常少:考虑效率、性能、维护等问题
在大中型的框架中可以看到--数据分析、报表设计、科学计算工具
站在巨人的肩膀【开源解析工具包】
Expression4J
MESP
JEP 声明
摘自秦小波《设计模式之禅》第2版;
仅供学习,严禁商业用途;
代码手写,没有经编译器编译,有个别错误,自行根据上下文改正;
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮