Java 设计模式之解释器模式
一、定义
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。
主要角色:
抽象表达式角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
终结符表达式角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
非终结符表达式角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
环境角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
客户端:主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
二、代码实现
举例:比如我们要定义一个“+”,“-”的操作,通过表达式角色中的解释器来完事具体的加减操作。
抽象表达式:
public abstract class AbstractExpression { public abstract int interpret(Context context); }
终结表达式角色:加减的数据变量
public class VariableExpression extends AbstractExpression{ //声明存储变量名 private String name; public VariableExpression(String name) { this.name = name; } @Override public int interpret(Context context) { return context.getValue(this); } @Override public String toString() { return name; } }
非终结表达式角色:加法
public class PlusExpression extends AbstractExpression{ //左边表达式 变量 private AbstractExpression left; //右边表达式 变量 private AbstractExpression right; public PlusExpression(AbstractExpression left, AbstractExpression right) { this.left = left; this.right = right; } @Override public int interpret(Context context) { //返回左边的值 + 右边的值 return left.interpret(context)+right.interpret(context); } }
非终结表达式角色:减法
public class MinusExpression extends AbstractExpression{ //左边表达式 变量 private AbstractExpression left; //右边表达式 变量 private AbstractExpression right; public MinusExpression(AbstractExpression left, AbstractExpression right) { this.left = left; this.right = right; } @Override public int interpret(Context context) { //返回左边的值 - 右边的值 return left.interpret(context)-right.interpret(context); } }
环境角色:
public class Context { //存储变量及对应的值 private Mapmap = new HashMap<>(); //添加变量 public void addVar(VariableExpression variable, Integer value){ map.put(variable,value); } //根据变量获取对应的值 public int getValue(VariableExpression variable){ return map.get(variable); } }
客户端使用:
public static void main(String[] args) { //定义环境类 Context context = new Context(); //定义变量 VariableExpression a = new VariableExpression("a"); VariableExpression b = new VariableExpression("b"); VariableExpression c = new VariableExpression("c"); //将变量添加到环境类 context.addVar(a,1); context.addVar(b,2); context.addVar(c,3); //运算 a+b-3 即1+2-3 AbstractExpression expression = new PlusExpression(a,new MinusExpression(b,c)); //结果 int res = expression.interpret(context); System.out.println("结果为:"+res); }
输出:
结果为:0
三、总结
优点:
扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点:
执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
使用场景:
当语言的文法较为简单,且执行效率不是关键问题时。
当问题重复出现,且可以用一种简单的语言来进行表达时。
当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。