您的位置:首页 > 其它

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

2017-08-17 14:43 375 查看


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

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: