`
wangwengcn
  • 浏览: 172955 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

21.解析器模式(Interpreter Pattern)

阅读更多

1.定义

给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。
属于行为类模式。

 

2.解释器模式的使用场景

  • 重复发生的问题可以使用解释器模式:比如根据用户输入的公式进行加减乘除四则运算,但是他们输入的公式每次都不同,有时是a+b-c*d,有时是a*b+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。
  • 一个简单语法需要解释的场景:你看看下面给出的例子,非终结表达式,文法规则越多,复杂度越高,而且类间还要进行递归调用。因此解释器模式一般用来解析比较标准的字符集,例如sql语法分析,不过该部分逐渐被专用工具所取代。

在实际开发工作中经常会有客户要求计算公式他们自己制定,我们只要按照他们的公式和提供的数据进行运算就行了,那么这个业务逻辑也就不能写死了。
下面让我们工具加减乘除四则运算的例子来看看何谓解释器模式(四则比较麻烦,例子只写加减操作):

 

package _21InterpretePattern;

import java.util.HashMap;

/**
 * 抽象表达式
 */
public abstract class Expression {
	
	/**
	 * 解析公式和数值,其中var中的key是公式的参数,value值是具体的数字
	 * 负责对传递进来的参数和值进行解析和匹配,其中key是表达式a+b+c中的a、b、c,value是运算时取得的值
	 */
	// 如果是终结符表达式,那么此方法将获取参数的值
	// 如果是非终结符表达式,那么此方法将进行运算,比如加减
	public abstract int interpreter(HashMap<String, Integer> var);
}
 
package _21InterpretePattern;

import java.util.HashMap;

/**
 * 变量解析器
 * 终结符表达式
 */
public class VarExpression extends Expression {
	
	private String key;

	public VarExpression(String key) 
	{
		this.key = key;
	}

	// 从map中取得值
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return var.get(key);
	}

}
 
package _21InterpretePattern;

/**
 * 抽象运算符解析器
 * 非终结符表达式
 */
public abstract class SymbolExpression extends Expression {
	
	// 每个运算符都有左右两个参数进行运算,因此抽象到父类中
	protected Expression left;
	protected Expression right;

	public SymbolExpression(Expression left, Expression right)
	{
		this.left = left;
		this.right = right;
	}

}
 
package _21InterpretePattern;

import java.util.HashMap;

/**
 * 加法解析器
 * interpreter方法处理加法运算
 */
public class AddExpression extends SymbolExpression {

	public AddExpression(Expression left, Expression right) {
		super(left, right);
	}

	// 进行加法运算
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return super.left.interpreter(var) + super.right.interpreter(var);
	}

}
 
package _21InterpretePattern;

import java.util.HashMap;

/**
 * 减法解析器
 * interpreter方法处理减法运算
 */
public class SubExpression extends SymbolExpression {

	public SubExpression(Expression left, Expression right) {
		super(left, right);
	}

	// 进行减法运算
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return super.left.interpreter(var) - super.right.interpreter(var);
	}

}
 
package _21InterpretePattern;

import java.util.HashMap;
import java.util.Stack;

/**
 * 对输入的表达式进行解析,并计算
 */
public class Context {

	// 定义表达式,最后拿到是一个运算解析器,比如X+Y格式的,其中X可能又是由A+B的运算解析器组成
	// 只有最底层的解析器才是变量解析器,也就是终结符表达式
	// 此参数最终得到的肯定是非终结表达式
	private Expression expression;
	
	/**
	 * 分析用户输入的表达式
	 */
	public void analyse(String expStr) {
		// 定义一个栈,安排运算的先后顺序
		Stack<Expression> stack = new Stack<Expression>();
		char[] charArray = expStr.toCharArray();
		Expression left = null;
		Expression right = null;
		for(int i=0; i<charArray.length; i++)
		{
			switch(charArray[i])
			{
			case '+':
				// 将加法运算加入到栈中
				left = stack.pop();
				right = new VarExpression(String.valueOf(charArray[++i]));
				stack.push(new AddExpression(left, right));
				break;
			case '-':
				// 将减法运算加入到栈中
				left = stack.pop();
				right = new VarExpression(String.valueOf(charArray[++i]));
				stack.push(new SubExpression(left, right));
				break;
			default:
				// 如果不是运算符,那么就是终结表达式
				stack.push(new VarExpression(String.valueOf(charArray[i])));
			}
		}
		// 把最终栈的顶层抛出,它即是最后封装的非终结表达式
		this.expression = stack.pop();
	}
	
	// 将键值对输入给表达式运算
	public int run(HashMap<String, Integer> var)
	{
		return expression.interpreter(var);
	}
}
 
package _21InterpretePattern;

import java.util.HashMap;

/**
 * 场景类
 */
public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String expStr = "a+b-c+d";
		HashMap<String, Integer> var = new HashMap<String, Integer>();
		var.put("a", 1);
		var.put("b", 2);
		var.put("c", 2);
		var.put("d", 4);
		
		Context context = new Context();
		// 先解析运算表达式
		context.analyse(expStr);
		// 进行运算
		System.out.println(context.run(var));
	}

}

 

3.解释器模式的四个角色

  • AbstractExpression抽象解释器:声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpreter()方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。
  • TerminalExpression终结符表达式:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
  • NonterminalExpression非终结符表达式:文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
  • Context环境角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。

 

4.解释器模式的优点

解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只需要修改相应的非终结符就可以了,若扩展语法,只需要增加非终结符类就可以了。

 

5.解释器模式的缺点

  • 解释器模式会引起类的膨胀:每个语法都需要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来非常多的麻烦。
  • 解释器模式采用递归调用方法:每个非终结符表达式只关心与自己相关的表达式,每个表达式需要知道最终的结果,必须通过一层一层的剥茧,无论是面向对象的语言还是面向过程的语言,递归都是一个不推荐的方式(只在必要条件下使用),它将导致调试非常复杂。想想看,如果要排查一个错误,我们是不是要一个个断点调试下去,直至最小的语法单元。
  • 解释器模式使用了大量的循环和递归:效率是一个不容忽视的问题。特别是用于解释一个解析复杂、冗长的语法时,效率是难以忍受的。

6.解释器模式的注意事项

尽量不要在重要模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式、弥补Java编译型语言的不足。我们在一个银行的分析型项目中就采用了JRuby进行运算处理,避免使用解释器模式的四则运算,效率和性能各方面表现良好。

解释器模式在实际的系统开发中使用的非常少,因为它会引起效率、性能以及维护等问题,一般在大中型的框架型项目中能找到它的身影,如一些数据分析工具、报表设计工具、科学计算工具等,如果你确实遇到“一种特定类型的问题发生的频率足够高”的情况,准备使用解释器模式时,可以考虑一下Expression4J、MESP、Jep 等开源的解析工具包,功能都非常强大,而且非常容易使用,效率也不错,实现大多数的数学运算完全没有问题,自己没有必要重头开始编写解释器。

分享到:
评论

相关推荐

    C#设计模式_设计模式_C#_

    解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式(Strategy Pattern) 22. 访问者模式(Visitor ...

    C#设计模式(23种设计模式)

    解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式(Strategy Pattern) 22. 访问者...

    设计模式代码——c#

    17. 解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式(Strategy Pattern) 22. 访问者...

    23种设计模式 (创建型,结构型,行为型)

    解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式(Strategy Pattern) 22. 访问者...

    32种设计模式

    解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式(Strategy Pattern) ...

    设计模式之解释器模式(Interpreter Pattern)

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

    用Java实现23种设计模式

    解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式...

    设计模式精解(GoF 23种设计模式解析附C实现源码) pdf

    0.1 设计模式解析(总序)........................... 0.2 设计模式解析后记................................... 0.3 与作者联系.............................................. 1 创建型模式......................

    C#23种设计模式

    17. 解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式(Strategy Pattern) 22. 访问者模式...

    tm.rar_The Interpreter

    AML opcode information for the AML parser and interpreter.

    InterpreterPattern.rar

    使用Qt C++ 开发的计算器小程序,采用解析器、状态机两种模式, 是个人学习设计模式后的总结,希望有所帮助。

    YGRJ.rar_basic interpreter

    A tiny BASIC interpreter in c good

    BAS-INT2.rar_basic interpreter_tiny _tiny basic

    http://www.programmersheaven.com/zone6/cat700/16060.htm A tiny BASIC interpreter with C++ sourcecode

    设计模式精解-GoF 23 种设计模式解析

    0.1 设计模式解析(总序)...............2 0.2 设计模式解析后记.......................2 0.3 与作者联系..................................5 1 创建型模式.............................................5 ...

    (行为型模式) Interpreter 解释器模式

    C#面向对象设计模式 (行为型模式) Interpreter 解释器模式 视频讲座下载

    Writing.An.Interpreter.In.Go.epub

    We'll start with 0 lines of code and end up with a fully working interpreter for the Monkey* programming language. Step by step. From tokens to output. All code shown and included. Fully tested. ...

    python指南_v2.6_简体中文版

    2. Using the Python Interpreter 使用 Python 解释器 ................... 2.1. Invoking the Interpreter 调用解释器 ............................ 2.1.1. Argument Passing 参数传递 .............................

    设计模式精解-GoF 23 种设计模式解析附 C++实现源码.rar

    设计模式精解-GoF 23 种设计模式解析附 C++实现源码 目 录 0 引言 ............................................................................................................................................

    C++设计模式代码资源25_Interpreter_解析器.zip

    C++设计模式代码资源25_Interpreter_解析器.zip

    设计模式精解-GoF 23种设计模式解析附C++实现源码.pdf

    设计模式精解-GoF 23种设计模式解析附C++实现源码 目 录 0 引言.........................................................................................................................................

Global site tag (gtag.js) - Google Analytics