设计模式之解释器模式笔记
- 说明
- Interpreter(解释器)
- 目录
- 解释器模式示例类图
- 抽象表达式类
- 环境角色类
- 封装变量的类
- 加法表达式类
- 减法表达式类
- 测试类
说明
记录下学习设计模式-解释器模式的写法。JDK使用版本为1.8版本。
Interpreter(解释器)
意图:定义一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
结构:
其中:
- AbstractExpression 声明一个程序的解释操作,这个接口为抽象语法树中所有的结点所共享。
- TerminalExpression 实现与文法中的终结符相关联的解释操作;一个句子中的每个终结符需要该类的一个实例。
- NonterminalExpression 对文法中的每一条规则都需要一个 NonterminalExpression类;为每个符号都维护一个 AbstractExpression类型的实例变量;为文法中的非终结符实现解释(Interpret)操作。
- Context包含解释器之外的一些全局信息。
- Client构建(或被给定)表示该文法定义的语言中一个特定的句子的抽象语法树,该抽象语法树由NonterminaExpression和TerminalExpression的实例装配而成;调用解释操作。
适用性:
- 当语言的文法较为简单,且执行效率不是关键问题时。
- 当问题重复出现,且可以用一种简单的语言来进行表达时。
- 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。
目录
解释器模式示例类图
以该UML类图实现解释器模式示例。
抽象表达式类
package com.example.deesign_patterns.interpreter;
//抽象表达式类
public abstract class AbstractExpression {
public abstract int interpret(Context context);
}
环境角色类
package com.example.deesign_patterns.interpreter;
import java.util.HashMap;
import java.util.Map;
//环境角色类
public class Context {
//定义一个map集合,用来存储变量及对应的值
private Map<Variable,Integer> map=new HashMap<Variable,Integer>();
//添加变量的功能
public void assign(Variable var,Integer value){
map.put(var,value);
}
//根据变量获取对应的值
public int getValue(Variable var){
return map.get(var);
}
}
封装变量的类
package com.example.deesign_patterns.interpreter;
//封装变量的类
public class Variable extends AbstractExpression{
//声明存储变量名的成员变量
private String name;
public Variable(String name) {
this.name = name;
}
@Override
public int interpret(Context context) {
//直接返回变量的值
return context.getValue(this);
}
@Override
public String toString() {
return name;
}
}
加法表达式类
package com.example.deesign_patterns.interpreter;
//加法表达式类
public class Plus extends AbstractExpression{
//加号左边的表达式
private AbstractExpression left;
//加号右边的表达式
private AbstractExpression right;
public Plus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
//将左边表达式的结果和右边表达式的结果进行相加
return left.interpret(context)+right.interpret(context);
}
@Override
public String toString() {
return "("+left.toString()+"+"+right.toString()+")";
}
}
减法表达式类
package com.example.deesign_patterns.interpreter;
//减法表达式类
public class Minus extends AbstractExpression{
//减法左边的表达式
private AbstractExpression left;
//减法右边的表达式
private AbstractExpression right;
public Minus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
//将左边表达式的结果和右边表达式的结果进行相减
return left.interpret(context)-right.interpret(context);
}
@Override
public String toString() {
return "("+left.toString()+"-"+right.toString()+")";
}
}
测试类
package com.example.deesign_patterns.interpreter;
//测试类
public class Client {
public static void main(String[] args) {
//创建环境对象
Context context=new Context();
//创建多个变量对象
Variable a=new Variable("a");
Variable b=new Variable("b");
Variable c=new Variable("c");
Variable d=new Variable("d");
//将变量存储到环境对象中
context.assign(a,1);
context.assign(b,2);
context.assign(c,3);
context.assign(d,4);
//获取抽象语法树,如:(a-((b-c)+d))
AbstractExpression expression=new Minus(a,new Plus(new Minus(b,c),d));
//解释(计算)
int result=expression.interpret(context);
System.out.println(expression+"="+result);
}
}
好处:
- 易于改变和扩展文法。由于在解程器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
- 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。
- 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合"开闭原则"。
缺点:
- 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。
- 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。