Earth Guardian

You are not LATE!You are not EARLY!

0%

设计模式--行为型:解释器模式

解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子

解释器模式 Interpreter

解释器模式 Interpreter[ɪnˈtɜ:rprɪtə] Pattern:主要用来按指定规则解析一串语句,如:解析歌谱,XML,算术运算,机器人动作指令等等。

类图结构

0060-Interpreter-uml-classdiag.png

结构解析

  • AbstractExpression
    解释器抽象类,定义抽象解析方法。接口中主要是一个 interpret() 方法,称为解释操作。
  • TerminalExpression
    实现类,表示终止符,即实际被解释的数据。如算术运算的数字,曲谱的音符等。
  • NonTerminalExpression
    实现类,表示如何解释。如算术运算的加减乘除,曲谱的音阶音速等。
  • Context
    上下文,全局信息,或者辅助类。将表达式分解出来,把数据和解释器分离并执行。
  • Client
    客户端。

示例

逆波兰表达式

逆波兰表达式 Reverse Polish Notation 又叫做后缀表达式。逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如:(a+b)*(c+d) 转换为 ab+cd+*
逆波兰表达式操作十分简单,入栈和出栈就可以解决任何普通表达式的运算。其运算方式如下:如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

1
2
3
4
5
a+b         ---> ab+
a+(b-c) ---> abc-+
a+(b-c)*d ---> abc-d*+
a+d*(b-c) ---> adbc-*+
a=1+3 ---> a13+=

代码实现

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
// 1. AbstractExpression
public abstract class AbstractExpression {
public abstract int interpreter(Context context);
}

// 2. TerminalExpression
public class Constant extends AbstractExpression{
private int value = 0;

public Constant(int i){
value = i;
}

@Override
public int interpreter(Context context) {
return value;
}
}

// 3. NonTerminalExpression
public class Plus extends AbstractExpression{
private AbstractExpression left;
private AbstractExpression right;

public Plus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}

@Override
public int interpreter(Context context) {
return left.interpreter(context) + right.interpreter(context);
}
}

public class Minus extends AbstractExpression{
private AbstractExpression left;
private AbstractExpression right;

public Minus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}

@Override
public int interpreter(Context context) {
return left.interpreter(context) - right.interpreter(context);
}
}

public class Multiply extends AbstractExpression{
private AbstractExpression left;
private AbstractExpression right;

public Multiply(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}

@Override
public int interpreter(Context context) {
return left.interpreter(context) * right.interpreter(context);
}
}

// 4. Context
public class Context {
private Stack<Integer> mExpStack;
private String mExpression;

public Context(String expression){
mExpression = expression;
}

public int calculate(){
if (mExpression == null){
mExpression = "";
}

mExpStack = new Stack<>();
int value = 0;
Constant left, right;
for (char c : mExpression.toCharArray()){
switch (c){
case '+':
right = new Constant(pop());
left = new Constant(pop());
Plus plus = new Plus(left, right);
value = plus.interpreter(this);
break;
case '-':
right = new Constant(pop());
left = new Constant(pop());
Minus minus = new Minus(left, right);
value = minus.interpreter(this);
break;
case '*':
right = new Constant(pop());
left = new Constant(pop());
Multiply multiply = new Multiply(left, right);
value = multiply.interpreter(this);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = c - '0';
break;
default:
break;
}
mExpStack.push(value);
}
return pop();
}

public Integer pop(){
return mExpStack.pop();
}
}

// 5. Test
public class TestInterpreter {
public static void main(String[] args) {
Context context = new Context("5364-*+");
int value = context.calculate();
System.out.println("value = " + value);
}
}

// 6. Result
// 5364-*+ --> 5+3*(6-4)
value = 11

总结

解释器模式在实际中很少使用,它最显著的优点就是扩展性:若扩展语法只需要增加非终结符类就可以了。而缺点是执行效率较低:由于在解释器模式中使用了递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。同时对于复杂文法需要增加很多解释类。

参考文档