解释器模式
一、前言
解释器模式解决的问题是:语法分析
解释器模式(InterpreterPattern)是一种按照规定语法进行解析的方案,在现在项目中使用较少.
二、基本概念
1.定义
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
2.登场角色
2.1 AbstractExpression(抽象解释器)
抽象解释器
2.2 TerminalExpression(终结符表达式)
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。
2.3 NonterminalExpression(非终结符表达式)
文法中的每条规则对应于一个非终结表达式。
如加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
2.4 Context(环境角色)
3.通用源码
3.1 Expression
抽象表达式是生成语法集合(也叫做语法树)的关键,每个语法集合完成指定语法解析任务,它是通过递归调用的方式,最终由最小的语法单元进行解析完成。
1 | public abstract class Expression { |
3.2 TerminalExpression
终结符表达式主要是处理场景元素和数据的转换。
1 | public class TerminalExpression extends Expression { |
3.3 NonterminalExpression
每个非终结符表达式都代表了一个文法规则,并且每个文法规则都只关心自己周边的文法规则的结果(注意是结果),因此这就产生了每个非终结符表达式调用自己周边的非终结符表达式,然后最终、最小的文法规则就是终结符表达式.
1 | public class NonterminalExpression extends Expression { |
3.4 Client
通常Client是一个封装类,封装的结果就是传递进来一个规范语法文件,解析器分析后产生结果并返回,避免了调用者与语法解析器的耦合关系。
1 | public class Client { |
4.优劣
4.1 优点
扩展性良好
4.2 缺点
(1)类膨胀
(2)递归调用引起的调试复杂、运行效率低
5.适用场景
(1)语法解析
6.注意事项
尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。
7.最佳实践
解释器模式在实际的系统开发中使用得非常少,因为它会引起效率、性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具、报表设计工具、科学计算工具等,若你确实遇到“一种特定类型的问题发生的频率足够高”的情况,准备使用解释器模式时,可以考虑一下Expression4J、MESP(MathExpressionStringParser)、Jep等开源的解析工具包,功能都异常强大,而且非常容易使用,效率也还不错。