设计模式解密(21)- 解释器模式

1、简介

定义:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

主要解决:对于一些固定文法构建一个解释句子的解释器。

本质:分离实现,解释执行。Interpreter模式其实就是一种简单的语法解释器构架。

英文:Interpreter

类型:行为型

2、类图及组成

(引)类图:


组成:
  AbstractExpression(抽象表达式):定义解释器的接口,约定解释器的解释操作。

  TerminalExpression(终结符表达式):用来实现语法规则中和终结符相关的操作,不再包含其它的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器。

  NonterminalExpression(非终结符表达式):用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其它的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的组合对象,可以有多种非终结符解释器。

  Context(上下文):它包含了解释器之外一些其他的全局信息;通常包含各个解释器需要的数据,或是公共的功能。

  Client(客户端):指的是使用解释器的客户端,通常在这里去把按照语言的语法做的表达式,转换成为使用解释器对象描述的抽象语法树,然后调用解释操作。

代码结构:

/**
 * 抽象表达式
 */
public abstract class AbstractExpression {
    /**
     * 解释的操作
     * @param ctx 上下文对象
     */
    public abstract void interpret(Context ctx);
}

/**
 * 终结符表达式
 */
public class TerminalExpression extends AbstractExpression{
    public void interpret(Context ctx) {
        //实现与语法规则中的终结符相关联的解释操作
    }
}

/**
 * 非终结符表达式
 */
public class NonterminalExpression extends AbstractExpression{
    public void interpret(Context ctx) {
        //实现与语法规则中的非终结符相关联的解释操作
    }
}

/**
 * 上下文,包含解释器之外的一些全局信息
 */
public class Context {

}

/**
 * 使用解释器的客户
 */
public class Client {
    //主要按照语法规则对特定的句子构建抽象语法树
    //然后调用解释操作
}

3、实例引入

场景: 与(and)、或(or)、非(not) 的一套解析器

抽象表达式:

package com.designpattern.Interpreter;

/**
 *  抽象表达式 -- BooleanExp
 *  定义:
 *      BooleanExp -->> VariableExp | Constant | OrExp | AndExp | NotExp | '(' BooleanExp ')'
 *      AndExp -->> BooleanExp 'and' BooleanExp
 *      OrExp -->> BooleanExp 'or' BooleanExp
 *      NotExp -->> BooleanExp 'not' BooleanExp
 *      Constant -->> 'true' | 'false'
 *      VariableExp -->> 'A' | 'B' | ... | 'Z'
 *      
 * @author Json<<json1990@foxmail.com>>
 */
public interface BooleanExp {
    
    public abstract boolean Evaluate(Context c);
    
    public abstract BooleanExp Replace(String var, BooleanExp exp);
    
    public abstract BooleanExp Copy();
}

3个具体表达式:

package com.designpattern.Interpreter;

/**
 * 非终结符表达式 -- and
 * @author Json<<json1990@foxmail.com>>
 */
public class AndExp implements BooleanExp {
    private BooleanExp operand1;
    private BooleanExp operand2;
    
    public AndExp(BooleanExp oper1, BooleanExp oper2) {
        operand1 = oper1;
        operand2 = oper2;
    }

    public boolean Evaluate(Context c) {
        return operand1.Evaluate(c) && operand2.Evaluate(c);
    }

    public BooleanExp Copy() {
        return new AndExp(operand1.Copy(), operand2.Copy());
    }

    public BooleanExp Replace(String var, BooleanExp exp) {
        return new AndExp(operand1.Replace(var, exp),operand2.Replace(var, exp));
    }
}
package com.designpattern.Interpreter;

/**
 * 非终结符表达式 -- or
 * @author Json<<json1990@foxmail.com>>
 */
public class OrExp implements BooleanExp {
    private BooleanExp operor1;
    private BooleanExp operor2;
    
    public OrExp(BooleanExp oper1, BooleanExp oper2) {
        operor1 = oper1;
        operor2 = oper2;
    }

    public boolean Evaluate(Context c) {
        return operor1.Evaluate(c) || operor2.Evaluate(c);
    }

    public BooleanExp Copy() {
        return new OrExp(operor1.Copy(), operor2.Copy());
    }

    public BooleanExp Replace(String var, BooleanExp exp) {
        return new OrExp(operor1.Replace(var, exp),operor2.Replace(var, exp));
    }
}
package com.designpattern.Interpreter;

/**
 * 非终结符表达式 -- not
 * @author Json<<json1990@foxmail.com>>
 */
public class NotExp implements BooleanExp {
    private BooleanExp opernot1;
    
    public NotExp(BooleanExp oper1) {
        opernot1 = oper1;
    }

    public boolean Evaluate(Context c) {
        return !(opernot1.Evaluate(c));
    }

    public BooleanExp Copy() {
        return new NotExp(opernot1.Copy());
    }

    public BooleanExp Replace(String var, BooleanExp exp) {
        return new NotExp(opernot1.Replace(var, exp));
    }
}

终结符表达式:

package com.designpattern.Interpreter;

/**
 * 终结符表达式
 * @author Json<<json1990@foxmail.com>>
 */
public class VariableExp implements BooleanExp {
    private String name;
    
    public VariableExp(String _name) {
        name = _name;
    }

    public boolean Evaluate(Context c) {
        return c.LookUp(name);
    }

    public BooleanExp Copy() {
        return new VariableExp(name);
    }

    public BooleanExp Replace(String var, BooleanExp exp) {
        if(var.equals(name)) {
            return exp.Copy();
        } else {
            return new VariableExp(name);
        }
    }
}

 上下文:

package com.designpattern.Interpreter;

import java.util.Hashtable;

/**
 * 上下文
 * @author Json<<json1990@foxmail.com>>
 */
public class Context  {
    private Hashtable<String, Boolean> context = new Hashtable<String, Boolean>();
    
    public void Assign(String name, boolean val) {
        context.put(name, new Boolean(val));
    }
    
    public boolean LookUp(String name) {
        return ((Boolean)context.get(name)).booleanValue();
    }
    
    public Context() {
    }
}

客户端测试:

package com.designpattern.Interpreter;

/**
 * 客户端测试
 * @author Json<<json1990@foxmail.com>>
 */
public class Client  {
    public static void main(String[] args) {
        Context context = new Context();
       
        VariableExp x = new VariableExp("X");
        VariableExp y = new VariableExp("Y");
        VariableExp z = new VariableExp("Z");

        context.Assign("X", true);
        context.Assign("Y", false);
        context.Assign("Z", true);
        
        //测试表达式 :(z and x) and (y and (not x))
        BooleanExp expression = new AndExp(new AndExp(z, x),new AndExp(y, new NotExp(x)));
        boolean result = expression.Evaluate(context);
        System.out.println("(z and x) and (y and (not x))表达式结果:" + result);
        
        //测试表达式 :(z and x) or (y and (not x))
        BooleanExp expression1 = new OrExp(new AndExp(z, x),new AndExp(y, new NotExp(x)));
        boolean result1 = expression1.Evaluate(context);
        System.out.println("(z and x) or (y and (not x))表达式结果:" + result1);
    }
}

结果:

(z and x) and (y and (not x))表达式结果:false
(z and x) or (y and (not x))表达式结果:true

4、优缺点

优点:

  易于实现语法:在解释器模式中,一条语法规则用一个解释器对象来解释执行,对于解释器的实现来讲,功能就变得比较简单,只需要考虑这一条语法规则的实现就好了,其它的都不用管。

  易于扩展新的语法:正是由于采用一个解释器对象负责一条语法规则的方式,使得扩展新的语法非常容易,扩展了新的语法,只需要创建相应的解释器对象,在创建抽象语法树的时候使用这个新的解释器对象就可以了。

 

缺点:

  引起类爆炸:每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。

  采用递归调用方法:每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个一个断点的调试下去,直到最小的语法单元。

  效率问题:解释器模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是非常低的。

5、场景

当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式。

注意:
  1、语法相对应该比较简单,太复杂的语法不合适使用解释器模式;

  2、效率要求不是很高,对效率要求很高的情况下,不适合使用解释器模式。

6、总结

解释器模式在实际的系统开发中使用的非常少,因为它会引起效率、性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,比如一些数据分析工具、报表设计工具、科学计算工具等等。

PS:数学运算常用运算解析工具:Expression4J、MESP(Math Expression String Parser)、Jep等开源的解析工具包,功能都异常强大,而且非常容易使用,效率也还不错!!!

 

PS:源码地址   https://github.com/JsonShare/DesignPattern/tree/master 

   

PS:原文地址  http://www.cnblogs.com/JsonShare/p/7367535.html

  

posted @ 2017-08-16 11:56  Json_wangqiang  阅读(1098)  评论(3编辑  收藏  举报