一、引言
23种设计模式大概分为三大类:
5种(创建型模式):工厂方法模式、抽象工厂模式、单例模式、原型模式、建造者模式。
7种(结构型模式):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
11种(行为型模式):策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
行为型又可以通过类与类之间的关系进行划分 :
解释器模式基本介绍:
- 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
- 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
- 应用场景
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 一些重复出现的问题可以用一种简单的语言来表达
- 一个简单语法需要解释的场景
对原理类图的说明:
-
Context: 是环境角色,含有解释器之外的全局信息
-
AbstractExpression: 抽象表达式,声明一个抽象的解释操作interpret(),这个方法为抽象语法树中所有的节点所共享
-
TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作
-
NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作
说明:输入 Context 和 TerminalExpression 信息通过 Client 输入即可
三、具体需求 1.四则运算问题-
先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复
-
再分别输入 a ,b, c, d, e 的值
-
最后求出结果:如图(简单起见,只考虑加减)
传统方式的问题分析:
- 编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析,得到结果
- 问题分析:如果加入新的运算符,比如 * / () 等等,不利于扩展;另外让一个方法来解析会造成程序结构混乱,不够清晰
- 解决方案:可以考虑使用解释器模式,即:表达式 ->解释器(可以有多种)->结果
思路分析 - 类图
说明:
- Expression:抽象类
- VarExpression:针对变量的解释器
- SymbolExpression:针对符号的解释器(非终结,即它还有子类)
具体实现
// 抽象类表达式:通过HashMap的键值对可以获取各个变量的值ֵ Expression.java
public abstract class Expression {
//解释公式和数值的关系:key就是公式(表达式,如a + b - c) 参数(如a,b,c..)
//value就是具体的值ֵ,即最终HashMap中存放的就是各个变量的值,如HashMap {a=10, b=20}
public abstract int interpreter(HashMap var);
}
// 变量的解析器 VarExpression.java
public class VarExpression extends Expression {
private String key; // key=a,key=b,key=c
public VarExpression(String key) {
this.key = key;
}
// var 得到的就是 {a=10, b=20}
// interpreter的作用:根据变量的名称返回对应的值ֵ
@Override
public int interpreter(HashMap var) {
return var.get(this.key);
}
}
// SymbolExpression.java
/**
* 抽象运算符号解析器:
* 1)每个运算符号都只和自己的左右两个数字有关系
* 2)同时,左右两个数字可能也是一个解析的结果
* 3)无论何种类型,都是Expression的实现类
*/
public class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
//因为SymbolExpression是让其子类来实现的,因此interpreter()是一个默认实现
@Override
public int interpreter(HashMap var) {
return 0;
}
}
//加法解释器 AddExpression.java
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
//处理“+”
//var即{a=10,b=20...}(值key)
public int interpreter(HashMap var) {
//super.left.interpreter(var):返回left表达式对应的值,比如a对应的值是10
//super.right.interpreter(var):返回right表达式对应的值,比如b对应的值是20
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
//减法解释器 SubExpression.java
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
//求出left与right表达式相减的结果
public int interpreter(HashMap var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
// Calculator.java
public class Calculator {
//定义表达式
private Expression expression;
//构造函数传参 并解析
public Calculator(String expStr) { //如传入的expStr = a+b
// 安排运算先后顺序
Stack stack = new Stack();
// 表达式拆分成字符数组
char[] charArray = expStr.toCharArray();//expStr拆分为 [a, +, b]
Expression left = null;
Expression right = null;
//遍历字符数组,即遍历 [a, +, b],针对不同的情况做相应的处理
for (int i = 0; i "a"
right = new VarExpression(String.valueOf(charArray[++i]));// 取出left表达式 => "b"
stack.push(new AddExpression(left, right));// 根据得到的left和right对象构建 AddExpresson 并加入stack中
break;
case '-': //
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
default:
//如果是一个Var(变量),就创建一个VarExpression对象,并push到栈中
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
//当遍历完整个charArray数组后,stack中就得到了最后的Expression
this.expression = stack.pop();
}
public int run(HashMap var) {
//将表达式a+b和var:{a=10,b=20}绑定
//然后传递给expression的interpreter进行处理/解释执行
return this.expression.interpreter(var);
}
}
// ClientTest.java
public class ClientTest {
public static void main(String[] args) throws IOException {
String expStr = getExpStr(); // 如:a+b
HashMap var = getValue(expStr);// var:{a=10, b=20}
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
}
//获得表达式
public static String getExpStr() throws IOException {
System.out.print("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
//获得值映射
public 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.print("请输入" + String.valueOf(ch) + "的值:");
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
}
四、注意事项和细节
- 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
- 应用场景:编译器、运算表达式计算、正则表达式、机器人等
- 使用解释器可能带来的问题:解释器模式会引起类膨胀;解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。(解释器一般用于解决比较复杂的问题,要慎用)