解释器模式(Interpretor):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
UML图:
|和&表达式的简单解释器代码
package com.thpin.repository.designpattern;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Pattern;
public class InterpretorDemo {
public static void main(String[] args) throws Exception {
ExpressionContext context = new ExpressionContext("f|f|t&f");
System.out.println(context.result());
}
}
/*
* 解释器抽象类
*/
abstract class AbstractExpression {
public abstract void interpret(ExpressionContext context);// 解释语法
public abstract boolean result(ExpressionContext context);// 获取结果
}
/*
* 表达式上下文 对 ‘|’ 和 ‘&’ 条件表达式解释
*/
class ExpressionContext {
private String input;// 输入
private Stack<AbstractExpression> expressions = new Stack<>();// 表达式栈,包括终结表达式和非终结表达式
private Map<String, Boolean> map = new HashMap<>(); // 终结符字典
// 校验输入、input初始化、字典初始化
public ExpressionContext(String input) throws Exception {
if (!isMatch(input)) {
throw new Exception("表达式不合法,t或f 和 |或& 相间组合,并以t 或 f 开头结尾");
}
this.input = input;
map.put("t", true);
map.put("f", false);
}
// 校验
private boolean isMatch(String input) {
String regex = "([t|f][||&])+[t|f]";
return Pattern.matches(regex, input);
}
// 获取终结符的真实值
public boolean search(String key) {
return map.get(key);
}
// 获取表达式input的结果
public boolean result() {
new TerminalExpression().interpret(this);// 首字符肯定是t 或 f,用终结符解释器,入栈
removeFirstChar();// 去头(t 或 f)
while (input.length() > 0) {
String firstChar = removeFirstChar();// 去头(| 或 &)
new TerminalExpression().interpret(this);// 此时肯定是t 或 f,用终结符解释器,入栈
removeFirstChar();// 去头(t 或 f)
if ("&".equals(firstChar)) {
new AndExpression().interpret(this);// 取出栈顶两个解释器生成AND解释器再入栈
}
}
while (expressions.size() > 1) {// 这时&的操作已结束,进行|操作
new OrExpression().interpret(this);// 取出栈顶两个解释器生成OR解释器再入栈
}
return expressions.pop().result(this);// 栈底只有一个嵌套结构的解释器,调用其result()获取最终结果
}
// input去掉首字符,并返回首字符
private String removeFirstChar() {
String firstChar = input.substring(0, 1);
input = input.substring(1);
return firstChar;
}
// 获取栈
public Stack<AbstractExpression> getExpressions() {
return expressions;
}
// 获取输入
public String getInput() {
return input;
}
}
/*
* 终结符解释器,对应表达式 t 、 f
*/
class TerminalExpression extends AbstractExpression {
private String value;
// 将自己保存到context的解释器栈栈顶
public void interpret(ExpressionContext context) {
this.value = context.getInput().substring(0, 1);
context.getExpressions().push(this);
}
public boolean result(ExpressionContext context) {
return context.search(value);
}
}
/*
* 终结符and解释器, 对应表达式例如 t&f
*/
class AndExpression extends AbstractExpression {
protected AbstractExpression expression1;
protected AbstractExpression expression2;
// 将自己保存到context的解释器栈栈顶
public void interpret(ExpressionContext context) {
Stack<AbstractExpression> expressions = context.getExpressions();
expression1 = context.getExpressions().pop();
expression2 = context.getExpressions().pop();
expressions.push(this);
}
public boolean result(ExpressionContext context) {
return expression1.result(context) & expression2.result(context);
}
}
/*
* 终结符or解释器,对应表达式例如 t|f
* 与and结构相同,只是返回结果用 | 做计算
*/
class OrExpression extends AndExpression {
public boolean result(ExpressionContext context) {
return expression1.result(context) | expression2.result(context);
}
}
结果:
false
在ExpressionContext 的 result()方法最后一行
return expressions.pop().result(this);
这里打断点,可以看到'f|f|t&f'被解释后生成的解释器是这样的结构:
最后解释器栈里只有一个Or解释器,而其属性分别是一个Or和一个Ter,Or又包含一个And和一个Ter,And又包含两个Ter,这正是一个 'f|f|t&f'表达式的计算顺序,只要符合语法规则随意改变表达式,就会产生不同的解释器组合结构,比如‘t&t|t&f’
解释器结构变成了一个Or里面两个And,这正是我们想要的结果。
如果一种特定问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法时,可使用解释器模式。
解释器模式可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。
解释器模式也有不足,解释为文法中的每一条规则至少定义一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译生成器来处理。jvm就是用编译器将.java文件编译成易于解释器理解的.class文件,然后经解释器解释运行。