设计模式解密(21)- 解释器模式
Posted 与其临渊羡鱼,不如退而结网
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式解密(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
以上是关于设计模式解密(21)- 解释器模式的主要内容,如果未能解决你的问题,请参考以下文章