设计模式之解释器模式
1. 什么是解释器模式
Interpreter模式也叫解释器模式,是行为模式之一,它是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法。简单地说,Interpreter模式是一种简单的语法解释器构架。
换一种解释就是定义一个语法, 定义一个解释器,该解释器处理该语法句子将某些复杂问题,表达为某种语法规则,然后构建解释器来解释处理这类句子。
解释器模式的结构:
-
Context:解释器上下文环境类。用来存储解释器的上下文环境,比如需要解释的文法等。
-
AbstractExpression:解释器抽象类。
-
ConcreteExpression:解释器具体实现类。
优缺点:
优点:
-
容易修改,修改语法规则只要修改相应非终结符即可
-
扩展方便,扩展语法,只要增加非终结符类即可
缺点:
-
对于复杂语法的表示会产生复杂的类层次结构,不便管理和维护
-
解释器采用递归方式,效率会受影响
一般的适用场景:
-
当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
-
该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
-
效率不是一个关键问题,最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
使用时的注意事项:
-
尽量不要在重要的模块中使用解释器模式
-
解释器模式在实际的系统开发中使用的非常少
-
可以考虑一下Expression4J、MESP、Jep等开源的解析工具包
2. 具体的实例
有一个大数据的项目,大数据统计项目遇到了问题:
按照计算模型对现有数据统计、分析、预测,一般的计算模型是一个或多个运算公式,通常是加减乘除四则运算,设计方案要有高扩展性。
计算模型的设计:
计算模型按正常算术方式书写,解释器处理语法逻辑
计算模型里有两类符号:数据和计算符
用逆波兰算法分析算式语法
用解释器模式处理数据。
这里项目中其实就是要使用解释器来计算符号公式。 解释的意思就是用一种方式或语法来执行他。
项目设计的类图如下所示:
具体的代码如下:
AbstractExpresstion:抽象表达式:
public abstract class AbstractExpresstion {
public abstract Float interpreter(HashMap<String, Float> var);
}
VarExpresstion:
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);
}
}
具体的”加减乘除“运算符:
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);
}
}
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);
}
}
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);
}
}
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);
}
}
逆波兰实现,具体就是统一的实现运算的,让计算表达式转换成为逆波兰表达式,然后可以实现按运算符运算顺序规则的运算。
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();
}
}
}
底部的计算器:
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!!!");
}
}
}
测试类:
public class MainTest {
public static void main(String[] args) {
new Calculator();
}
}
看一下输出吧,就是实现了表达式的运算: