Fork me on GitHub

设计模式之解释器模式

设计模式之解释器模式

1. 什么是解释器模式

Interpreter模式也叫解释器模式,是行为模式之一,它是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法。简单地说,Interpreter模式是一种简单的语法解释器构架。

换一种解释就是定义一个语法, 定义一个解释器,该解释器处理该语法句子将某些复杂问题,表达为某种语法规则,然后构建解释器来解释处理这类句子。

解释器模式的结构:

1

  • Context:解释器上下文环境类。用来存储解释器的上下文环境,比如需要解释的文法等。
  • AbstractExpression:解释器抽象类。
  • ConcreteExpression:解释器具体实现类。

优缺点

优点:

  • 容易修改,修改语法规则只要修改相应非终结符即可
  • 扩展方便,扩展语法,只要增加非终结符类即可

缺点:

  • 对于复杂语法的表示会产生复杂的类层次结构,不便管理和维护
  • 解释器采用递归方式,效率会受影响

一般的适用场景

  • 当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
  • 该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
  • 效率不是一个关键问题,最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。

使用时的注意事项:

  • 尽量不要在重要的模块中使用解释器模式
  • 解释器模式在实际的系统开发中使用的非常少
  • 可以考虑一下Expression4J、MESP、Jep等开源的解析工具包

2. 具体的实例

有一个大数据的项目,大数据统计项目遇到了问题:

按照计算模型对现有数据统计、分析、预测,一般的计算模型是一个或多个运算公式,通常是加减乘除四则运算,设计方案要有高扩展性。

计算模型的设计:

计算模型按正常算术方式书写,解释器处理语法逻辑

计算模型里有两类符号:数据和计算符

用逆波兰算法分析算式语法

用解释器模式处理数据。

这里项目中其实就是要使用解释器来计算符号公式。 解释的意思就是用一种方式或语法来执行他。

项目设计的类图如下所示:

1539594118.png)

具体的代码如下:

AbstractExpresstion:抽象表达式:

1
2
3
public abstract class AbstractExpresstion {
public abstract Float interpreter(HashMap<String, Float> var);
}

VarExpresstion:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class VarExpresstion extends AbstractExpresstion {
private String key;

public VarExpresstion(String _key) {

this.key = _key;

}

@Override
public Float interpreter(HashMap<String, Float> var) {
// TODO Auto-generated method stub
return var.get(this.key);
}
}

具体的”加减乘除“运算符:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class AddExpresstion extends SymbolExpresstion {

public AddExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) {
super(_left, _right);
// TODO Auto-generated constructor stub
}

@Override
public Float interpreter(HashMap<String, Float> var) {
// TODO Auto-generated method stub
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SubExpresstion extends SymbolExpresstion {

public SubExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) {
super(_left, _right);
// TODO Auto-generated constructor stub
}

@Override
public Float interpreter(HashMap<String, Float> var) {
// TODO Auto-generated method stub
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MultiExpresstion extends SymbolExpresstion {

public MultiExpresstion(AbstractExpresstion _left,
AbstractExpresstion _right) {
super(_left, _right);
// TODO Auto-generated constructor stub
}

@Override
public Float interpreter(HashMap<String, Float> var) {
// TODO Auto-generated method stub
return super.left.interpreter(var) * super.right.interpreter(var);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class DivExpresstion extends SymbolExpresstion {

public DivExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) {
super(_left, _right);
// TODO Auto-generated constructor stub
}

@Override
public Float interpreter(HashMap<String, Float> var) {
// TODO Auto-generated method stub
return super.left.interpreter(var) / super.right.interpreter(var);
}
}

逆波兰实现,具体就是统一的实现运算的,让计算表达式转换成为逆波兰表达式,然后可以实现按运算符运算顺序规则的运算。

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
public class RPN {

private ArrayList<String> expression = new ArrayList<String>();// 存储中序表达式

private ArrayList<String> right = new ArrayList<String>();// 存储右序表达式

private AbstractExpresstion result;// 结果

// 依据输入信息创建对象,将数值与操作符放入ArrayList中
public RPN(String input) {
StringTokenizer st = new StringTokenizer(input, "+-*/()", true);
while (st.hasMoreElements()) {
expression.add(st.nextToken());
}
}

// 将中序表达式转换为右序表达式
private void toRight() {
Stacks aStack = new Stacks();
String operator;
int position = 0;
while (true) {
if (Calculate.isOperator((String) expression.get(position))) {
if (aStack.top == -1
|| ((String) expression.get(position)).equals("(")) {
aStack.push(expression.get(position));
} else {
if (((String) expression.get(position)).equals(")")) {
if (!((String) aStack.top()).equals("(")) {
operator = (String) aStack.pop();
right.add(operator);
}
} else {
if (Calculate.priority((String) expression
.get(position)) <= Calculate
.priority((String) aStack.top())
&& aStack.top != -1) {
operator = (String) aStack.pop();
if (!operator.equals("("))
right.add(operator);
}
aStack.push(expression.get(position));
}
}
} else
right.add(expression.get(position));
position++;
if (position >= expression.size())
break;
}
while (aStack.top != -1) {
operator = (String) aStack.pop();
right.add(operator);
}
}

// 对右序表达式进行求值
public void getResult(HashMap<String, Float> var) {
this.toRight();
Stack<AbstractExpresstion> stack = new Stack<AbstractExpresstion>();
AbstractExpresstion op1, op2;
String is = null;
Iterator it = right.iterator();

while (it.hasNext()) {
is = (String) it.next();
if (Calculate.isOperator(is)) {
op2 = stack.pop();
op1 = stack.pop();
stack.push(Calculate.twoResult(is, op1, op2));
} else
stack.push(new VarExpresstion(is));
}
result = stack.pop();
it = expression.iterator();
while (it.hasNext()) {
System.out.print((String) it.next());
}
System.out.println("=" + result.interpreter(var));
}

public static class Calculate {
// 判断是否为操作符号
public static boolean isOperator(String operator) {
if (operator.equals("+") || operator.equals("-")
|| operator.equals("*") || operator.equals("/")
|| operator.equals("(") || operator.equals(")"))
return true;
else
return false;
}

// 设置操作符号的优先级别
public static int priority(String operator) {
if (operator.equals("+") || operator.equals("-")
|| operator.equals("("))
return 1;
else if (operator.equals("*") || operator.equals("/"))
return 2;
else
return 0;
}

// 做2值之间的计算
public static AbstractExpresstion twoResult(String op,
AbstractExpresstion a, AbstractExpresstion b) {
try {

AbstractExpresstion result = null;
if (op.equals("+"))
result = new AddExpresstion(a, b);
else if (op.equals("-"))
result = new SubExpresstion(a, b);
else if (op.equals("*"))
result = new MultiExpresstion(a, b);
else if (op.equals("/"))
result = new DivExpresstion(a, b);
else
;
return result;
} catch (NumberFormatException e) {
System.out.println("input has something wrong!");
return null;
}
}
}

// 栈类
public class Stacks {
private LinkedList list = new LinkedList();
int top = -1;

public void push(Object value) {
top++;
list.addFirst(value);
}

public Object pop() {
Object temp = list.getFirst();
top--;
list.removeFirst();
return temp;

}

public Object top() {
return list.getFirst();
}
}
}

底部的计算器:

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
public class Calculator {

public Calculator() {
float[][] dataSource = new float[3][6];
System.out.println("data source:");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 6; j++) {
dataSource[i][j] = (float) (Math.random() * 100);
System.out.print(dataSource[i][j] + ",");
}
System.out.println(";");
}

try {
System.out.println("Input a expression:");
BufferedReader is = new BufferedReader(new InputStreamReader(
System.in));
for (;;) {
String input = new String();
input = is.readLine().trim();
if (input.equals("q"))
break;
else {
RPN boya = new RPN(input);
HashMap<String, Float> var;
for (int i = 0; i < 3; i++) {
var = new HashMap<String, Float>();
var.put("a", dataSource[i][0]);
var.put("b", dataSource[i][1]);
var.put("c", dataSource[i][2]);
var.put("d", dataSource[i][3]);
var.put("e", dataSource[i][4]);
var.put("f", dataSource[i][5]);

boya.getResult(var);

}

}
System.out
.println("Input another expression or input 'q' to quit:");
}
is.close();
} catch (IOException e) {
System.out.println("Wrong input!!!");
}

}
}

测试类:

1
2
3
4
5
6
7
public class MainTest {

public static void main(String[] args) {

new Calculator();
}
}

看一下输出吧,就是实现了表达式的运算:

1539599418().png)

本文标题:设计模式之解释器模式

文章作者:WilsonSong

发布时间:2018年10月18日 - 14:10

最后更新:2018年10月18日 - 14:10

原始链接:https://songwell1024.github.io/2018/10/18/InterpreterPattern/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------