Design Patterns(十九) Interpreter

Interpreter pattern provides a way to evaluate language grammar or expression. This type of pattern comes under behavioral pattern. This pattern involves implementing an expression interface which tells to interpret a particular context. This pattern is used in SQL parsing, symbol processing engine etc.

前言

    解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

解释器模式

基本介绍:

1) 在编译原理中,一个算术表达式通过 词法分析器 形成词法单元,而后这些词法单元再通过 语法分析器 构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器;
2) 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式);
3) 应用场景:1. 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。2. 一些重复出现的问题可以用一种简单的语言来表达。3. 一个简单语法需要解释的场景;
4) 这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等。

角色介绍:

1)抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
2)终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
3)非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
4)环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
5)客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

解释器模式-UML图:


代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Stack;

/**
* @Auther: Arsenal
* @Date: 2020-03-21 12:53
* @Description: 解释器模式
*/
public class Interpreter {
public static void main(String[] args) throws IOException {
String expStr = getExpStr();
// 赋值
HashMap<String, Integer> var = getValue(expStr);
Calculator cal = new Calculator(expStr);
System.out.println("运算");
}

// 获得表达式
public static String getExpStr() throws IOException {
// 新建一个缓冲区,从缓冲区中读取数据
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}

// 获得值映射
public static HashMap<String, Integer> getValue(String expStr) throws IOException {
HashMap<String, Integer> map = new HashMap<String, Integer>();
// 解析
for (char ch : expStr.toCharArray()) {
if (ch != '+' && ch != '-') {
if (!map.containsKey(String.valueOf(ch))) {
// 从缓冲区中,读取整行
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
}
}
return map;
}
}

/**
* 抽象表达式类
*/
abstract class Expression {
// 解析数值,key是参数,value是数字
public abstract int interpreter(HashMap<String, Integer> var);
}

/**
* 变量解析器
*/
class VarExpression extends Expression {
private String key;

public VarExpression(String _key) {
this.key = _key;
}

// map取
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}

/**
* 抽象运算符号解析器
*/
abstract class SymbolExpression extends Expression {
// 左数值
protected Expression left;
// 右数值
protected Expression right;

// 初始化
public SymbolExpression(Expression _left, Expression _right) {
this.left = _left;
this.right = _right;
}
}

/**
* 对加法进行解析
*/
class AddExpression extends SymbolExpression {
public AddExpression(Expression _left, Expression _right) {
super(_left, _right);
}

// 进行相加
public int interpreter(HashMap<String, Integer> var) {
// 取得,两边的变量都保存在HashMap表中,根据left和right变量,进行相加
return super.left.interpreter(var) + super.right.interpreter(var);
}
}

/**
* 对减法进行解析
*/
class SubExpression extends SymbolExpression {
public SubExpression(Expression _left, Expression _right) {
super(_left, _right);
}

// 进行相减
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}

/**
* 再次对解析器进行封装
*/
class Calculator {
// 定义表达式
private Expression expression;

// 传参
public Calculator(String expStr) {
// 用于先后顺序
Stack<Expression> 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<String, Integer> var) {
return this.expression.interpreter(var);
}
}

总结

解释器模式的注意事项和细节:

1) 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性;
2) 应用场景:编译器、运算表达式计算、正则表达式、机器人等;
3) 使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。

延伸

    解释器模式
    设计模式-解释器模式
    解释器模式-菜鸟教程
    Design Patterns - Interpreter Pattern
    尚硅谷Java设计模式,韩顺平图解java设计模式

Content
  1. 1. 前言
  2. 2. 解释器模式
  3. 3. 总结
  4. 4. 延伸