设计模式之解释器模式
定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。这里的文法就是语言的语法规则。
结构
- AbstractExpression,抽象解释器角色,定义解释器的接口,约定解释器的解释操作。
- TerminalExpression,终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其他的解释器,类似组合模式中的叶子对象。
- UonterminalExpression,非终结符解释器,用来实现语法规则中和非终结符相关的操作,可以包含其他的解释器,类似组合模式中的组合对象。
- Context,上下文,通常包含各个解释器所需要的数据或是公共的功能。
简单实现
抽象解释器
public abstract class AbstractExpression {
abstract void interpret(Context context);
}
终结符解释器
public class TerminalExpression extends AbstractExpression {
@Override
void interpret(Context context) {
}
}
非终结符解释器
public class NonterminalExpression extends AbstractExpression {
@Override
void interpret(Context context) {
}
}
上下文对象
public class Context {
}
实现一个简单的计算器
支持加减法的简单计算
抽象解释器
import java.util.Map;
public abstract class AbstractExpression {
abstract int interpreter(Map<String, Integer> context);
}
终结符解释器
import java.util.Map;
public class VarExpression extends AbstractExpression {
private String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(Map<String, Integer> context) {
if (context.containsKey(key)) {
return context.get(key);
}
throw new IllegalArgumentException("not found field " + key);
}
}
变量解释器,根据变量名称获取变量值,map作为上下文。
非终结符解释器
public abstract class SymbolExpression extends AbstractExpression {
protected AbstractExpression left;
protected AbstractExpression right;
public SymbolExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
}
定义一个公共的操作符解释器,每个操作符都要有两个操作数,也就是变量
import java.util.Map;
public class AddExpression extends SymbolExpression {
public AddExpression(AbstractExpression left, AbstractExpression right) {
super(left, right);
}
//求出 left 和 right 表达式相加后的结果
public int interpreter(Map<String, Integer> context) {
return super.left.interpreter(context) + super.right.interpreter(context);
}
}
import java.util.Map;
public class SubExpression extends SymbolExpression {
public SubExpression(AbstractExpression left, AbstractExpression right) {
super(left, right);
}
//求出 left 和 right 表达式相减后的结果
public int interpreter(Map<String, Integer> context) {
return super.left.interpreter(context) - super.right.interpreter(context);
}
}
定义加法解释器和减法解释器
计算器
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
public class Calculator {
private AbstractExpression expression;
public Calculator(String expressionString) {
init(expressionString);
}
/**
* 表达式解析
*/
private void init(String expressionString) {
List<Character> charArray = infix2suffix(expressionString);
Stack<AbstractExpression> stack = new Stack<>();
for (char item : charArray) {
if (item == '+') {
if (stack.size() < 2) {
throw iae();
}
AbstractExpression right = stack.pop();
AbstractExpression left = stack.pop();
stack.push(new AddExpression(left, right));
} else if (item == '-') {
if (stack.size() < 2) {
throw iae();
}
AbstractExpression right = stack.pop();
AbstractExpression left = stack.pop();
stack.push(new SubExpression(left, right));
} else if (item <= 'z' && item >= 'a') {
stack.push(new VarExpression(String.valueOf(item)));
} else {
throw iae();
}
}
if (stack.size() != 1) {
throw iae();
}
this.expression = stack.pop();
}
/**
* 中缀表达式转后缀表达式
*/
private List<Character> infix2suffix(String infix) {
List<Character> res = new ArrayList<>();
Stack<Character> stack = new Stack<>();
Set<Character> operatorSet = Set.of('+', '-');
for (char item : infix.toCharArray()) {
//是字母,直接添加到结果列表
if (item <= 'z' && item >= 'a') {
res.add(item);
continue;
}
if (operatorSet.contains(item)) {
if (!stack.isEmpty()) {
res.add(stack.pop());
}
stack.push(item);
} else {
throw iae();
}
}
while (!stack.isEmpty()) {
res.add(stack.pop());
}
return res;
}
private IllegalArgumentException iae() {
return new IllegalArgumentException("illegal expression");
}
public int run(Map<String, Integer> var) {
return this.expression.interpreter(var);
}
}
a+b-c
转成
ab+c-
然后将表达式解析成一个树形结构的解释器
客户端
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Client {
public static void main(String[] args) throws IOException {
String expStr = "a+b-c";
Map<String, Integer> context = new HashMap<>();
context.put("a", 12);
context.put("b", 34);
context.put("c", 56);
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(context));
}
}
输出为
运算结果:a+b-c=-10
解释器模式在Spring中的实现
Spring中的SpelExpressionParser
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Client {
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("#a+#b-#c");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("a",12);
context.setVariable("b",34);
context.setVariable("c",56);
System.out.println(expression.getValue(context));//-10
}
}
表达式解析器SpelExpressionParser根据字符串解析成Expression对象,根据EvaluationContext上下文对象获取执行结果。
各种操作符如加号减号都是SpelNodeImpl对象。
总结
优点
- 一个解释器对应一条语法规则的方式,扩展新的语法非常容易。
缺点
- 不适合复杂的语法,复杂语法需要构建复杂的抽象语法树。
本质
解释器模式的本质是分离实现,解释执行。将语法规则分离成多个,一个解释器处理一个语法规则。
使用场景
- 当一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候。
- 语法规则比较简单
解释器模式在实际开发中应用很少,我们一般不需要自己去构建一种新的语法规则去解释。
参考
大战设计模式【20】—— 解释器模式
设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)
设计模式的征途—23.解释器(Interpreter)模式
解释器模式(详解版)
研磨设计模式-书籍