在一般开发中,我们很少还是用到解释器模式,但是我们都接触过,sql语句,正则,都是一个典型的解释器模式。
解释器模式中给定一个语言,定义它的规则和标识,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
先看一下解释器模式的一些成员的定义:
抽象解释器:具体的解释任务由各个实现类完成。
终结符表达式:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结表达式,但有多个实例,对应不同的终结符。
非终结符表达式:文法中的每条规则对应于一个非终结表达式,非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式
上下文:上下文环境类,包含解释器之外的全局信息
客户端::客户端,解析表达式,构建抽象语法树,执行具体的解释操作等.
当遇到非终结符表达式的时候,就继续往里面执行,当遇到终结符表达式的时候,直接返回结果过,有点像递归的感觉。
我们还是来举个栗子:
创建一个简单的加减法计算器,那么当不用运算的时候,也就是取值的时候,属于终结符表达式,而需要运算的地方,则为非终结符表达式
/**
* @author: hx
* @Time: 2019/5/24
* @Description: Context
*/
public class Context {
private HashMap mMap = new HashMap<>();
/**
* 定义变量对应关系
* @param expression
* @param integer
*/
public void push(Expression expression,Integer integer){
mMap.put(expression,integer);
}
/**
* 将变量转化为数字,直接返回结果,也属于终结符表达式
* @param expression
* @return
*/
public int talkOut(Expression expression){
return mMap.get(expression);
}
}
/**
* @author: hx
* @Time: 2019/5/24
* @Description: Expression
*/
public interface Expression {
int interpreter(Context context);
}
/**
* @author: hx
* @Time: 2019/5/24
* @Description: NonTeminalExpression
*/
public abstract class NonTeminalExpression implements Expression{
protected Expression mExpression1;
protected Expression mExpression2;
/**
* 非终结符表达式的父类,传入两个变量,子类对变量进行操作
* @param expression1
* @param expression2
*/
public NonTeminalExpression(Expression expression1, Expression expression2) {
mExpression1 = expression1;
mExpression2 = expression2;
}
}
/**
* @author: hx
* @Time: 2019/5/24
* @Description: Add
*/
public class Add extends NonTeminalExpression {
/**
* 非终结符表达式的父类,传入两个变量,子类对变量进行操作
*
* @param expression1
* @param expression2
*/
public Add(Expression expression1, Expression expression2) {
super(expression1, expression2);
}
@Override
public int interpreter(Context context) {
return mExpression1.interpreter(context)+mExpression2.interpreter(context);
}
}
/**
* @author: hx
* @Time: 2019/5/24
* @Description: Minus
*/
public class Minus extends NonTeminalExpression {
/**
* 非终结符表达式的父类,传入两个变量,子类对变量进行操作
*
* @param expression1
* @param expression2
*/
public Minus(Expression expression1, Expression expression2) {
super(expression1, expression2);
}
@Override
public int interpreter(Context context) {
return mExpression1.interpreter(context)-mExpression2.interpreter(context);
}
}
/**
* @author: hx
* @Time: 2019/5/24
* @Description: TeminalExpression
*/
public class TeminalExpression implements Expression{
/**
* 这里直接取出数据,不可能还有对数据做操作,所以可以看做是终结符表达式
* @param context
* @return
*/
@Override
public int interpreter(Context context) {
return context.talkOut(this);
}
}
运行一下缩略型计算器
public static void main(String[] args){
TeminalExpression t1 = new TeminalExpression();
TeminalExpression t2 = new TeminalExpression();
TeminalExpression t3 = new TeminalExpression();
Context context = new Context();
context.push(t1,5);
context.push(t2,6);
context.push(t3,7);
Integer integer = new Add(t3,new Minus(t2,t1)).interpreter(context);
System.out.println("integer="+integer);
}
输出结果:
integer=8
解释器模式,可能有点不太好理解,我第一次学习的时候,也是一脸懵,需要慢慢去研究,细细的分析和琢磨。
就上面的文法而言,解释一下最终的计算表达式
new Add(t3,new Minus(t2,t1)).interpreter(context)
当执行new Add().interpreter(context),括号里面参数为(t3,new Minus(t2,t1)),所以.interpreter(context)方法执行,要先创建Add对象,创建Add对象,传入t3和Minus对象,这里左边t3是一个终结符表达式,所以直接返回结果,右边的Minus对象中又传入t2和t1,所以Minus对象的左边和右边都是终结符表达式,但是,因为Add对象创建的时候,右边为Minus为非终结符表达式,所以,会深一步进入Minux对象中执行Minus的.interpreter(context)方法,可以理解为递归了一级,然后Minus的.interpreter(context)执行时,左右都是终结符表达式,所以返回结果,到Add的.interpreter(context)方法,可以看成t3..interpreter(context)+(t2..interpreter(context)-t1.interpreter(context))即7+(6-5),即是终结符表达式,所以返回结算结果。
有点绕,解释器模式在开发中基本不会使用到,除非是有一些非常特殊的公司做一些特殊的任务才会用到,到时候在工作中使用锻炼,也可以很快理解,这里就不再过多描述了。
优点:
1、可扩展性比较好,灵活。
2、增加了新的解释表达式的方式。
3、易于实现简单文法。
缺点:
1、可利用场景比较少。
2、对于复杂的文法比较难维护。
3、解释器模式会引起类膨胀。
4、解释器模式采用递归调用方法。