1 四則運算問題
現(xiàn)在有一個四則運算問題需要用代碼實現(xiàn):計算形如a+b-c的值,其中表達式的字母不能為空,并且每個字母都有數(shù)字與它對應,最后求出結果。
傳統(tǒng)的方法是寫一個方法,接收表達式的形式,然后根據(jù)用戶的輸入數(shù)值進行解析,最終計算出結果。但是這種方法不利于擴展,即加入新的運算符需要新增對應方法來解析,容易造成程序的混亂。為了更好地解決這個問題,可以考慮使用解釋器模式。
2 解釋器模式介紹
在編譯原理中,一個算術表達式通過詞法分析器形成詞法單元,然后這些詞法單元再通過語法分析器構建語法分析樹,最終形成一顆抽象的語法分析樹。這里的詞法分析器和語法分析器都可以稱之為解釋器。
解釋器模式(Interpreter Pattern):給定一個語言(表達式),定義它的文法的一種表示,并形成一個解釋器,使用該解釋器來解釋語言中的句子。
角色分析:
1)Context:環(huán)境上下文角色,包含解釋器之外額全局信息
2)AbstractExpression:抽象表達式,聲明一個抽象的解釋操作,這個方法為抽象語法樹中所有的節(jié)點共享
3)TerminalExpression:終結符表達式,實現(xiàn)與文法中的終結符相關的解釋操作
4)NonTerminalExpression:非終結符表達式,為文法中的非終結符實現(xiàn)解釋操作
5)Client:調用方,Context和TerminalExpression可以在這里輸入

3 代碼實現(xiàn)
首先有一個抽象表達式Expression:
// 表達式抽象類,通過Map獲取到變量的值
public abstract class Expression {
// 解釋公式和數(shù)值
public abstract int interpreter(Map<String, Integer> var);
}
終結符表達式繼承自Expression,對應到需求中的字母:
public class VarExpression extends Expression {
private String key; // key=a,b,c...
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(Map<String, Integer> var) {
return var.get(key);
}
}
非終結符表達式基類,對應運算符(+、-):
// 抽象的運算符號解析器,每個運算符號都只和自己左右兩個數(shù)字或表達式有關系
public abstract class SymbolExpression extends Expression{
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}
加法運算符表達式類:
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> var) {
return left.interpreter(var) + right.interpreter(var);
}
}
減法運算符表達式類:
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> var) {
return left.interpreter(var) - right.interpreter(var);
}
}
環(huán)境上下文類:
public class Calculator {
private Expression expression;
public Calculator(String expStr) {
Stack<Expression> stack = new Stack<>();
char[] chars = expStr.toCharArray();
Expression left = null;
Expression right = null;
for (int i = 0; i < chars.length; i++) {
switch (chars[i]) {
case '+':
left = stack.pop();
right = new VarExpression(String.valueOf(chars[++i]));
stack.push(new AddExpression(left, right));
break;
case '-':
left = stack.pop();
right = new VarExpression(String.valueOf(chars[++i]));
stack.push(new SubExpression(left, right));
break;
default:
stack.push(new VarExpression(String.valueOf(chars[i])));
}
}
expression = stack.pop();
}
public int run(Map<String, Integer> var) {
return expression.interpreter(var);
}
}
Client調用:
public class Client {
public static void main(String[] args) {
String expStr = "a+b-c";
Map<String, Integer> var = new HashMap<>();
var.put("a", 2);
var.put("b", 3);
var.put("c", 4);
Calculator calculator = new Calculator(expStr);
System.out.println("運算結果:" + expStr + " = " + calculator.run(var));
}
}
輸出結果:
運算結果:a+b-c = 1

4 解釋器模式的特點
1)當有一個語言需要解釋執(zhí)行,可將該語言中的句子表示為一個抽象的語法樹,此時可以考慮使用解釋器模式,讓程序具有良好的擴展性。
2)解釋器模式的使用場景:編譯器、運算表達式、正則表達式等。
3)使用解釋器可能會帶來類膨脹,即Context類可能會非常復雜,另外解釋器模式采用遞歸調用的方式,將會導致調試困難、執(zhí)行效率低下等問題。