一、定义
解释器模式(Interpreter Pattern)是一种按照规定语法进行解析的方案,例如解析四则运算、SQL语句等,现在项目中使用较少,其定义如下:
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
二、类图和角色
1. AbstractExpression 抽象解释器
具体解释任务由具体实现类完成。
public abstract class AbstractExpression {
/**
* 解释的操作
* @param ctx 上下文对象
*/
public abstract void interpret(Context ctx);
}
2. TerminalExpression 终结符表达式
例如 a + b
中的 a
和 b
,这些运算元素除了需要赋值外,不需要做任何处理,功能也基本相同,是语法中的最小单元,相当于组合模式中的叶子。
public class TerminalExpression extends AbstractExpression{
public void interpret(Context ctx) {
//实现与语法规则中的终结符相关联的解释操作
}
}
3. NonterminalExpression 非终结符表达式
例如 a + b
中的 +
号,这些运算符号都会对应一个具体的业务逻辑,例如加减乘除就是四个不同的非终结符表达式,相当于组合模式中的树枝。
public class NonterminalExpression extends AbstractExpression{
private List<AbstractExpression> list = new ArrayList<AbstractExpression>();
public void addAbstractExpression(AbstractExpression ae){
list.add(ae);
}
public void interpret(Context ctx) {
//实现与语法规则中的非终结符相关联的解释操作
}
}
4. Context 环境角色
存储各个解释器需要使用到的数据,或是公共功能。
public class Context {
//公共的属性方法
}
5. Client 客户端角色
使用解释器的客户端,通常在这里去把按照语言的语法做的表达式,转换成为使用解释器对象描述的抽象语法树,然后调用解释操作。
public class Client {
//第一大步:主要按照语法规则对特定的语句构建抽象语法树
//第二大步:然后调用解释操作
}
三、优缺点
- 优点:
- 容易修改(修改相应非终结符表达式)或扩展(新增相应非终结符表达式)语法。
- 缺点:
- 引起类的膨胀,每一个语法都要产生一个非终结符表达式,语法复杂时将会产生大量类文件。
- 循环的递归调用,带来的性能问题不容忽视,且难以调试。
四、使用场景
- 重复发生的问题
例如多个服务器产生的大量日志,需要对日志进行分类处理,文件格式可能不同但是数据要素都是一样的,按照解释器的说法就是终结符表达式都是一样的,不过非终结符表达式需要制定。 - 一些简单或者标准的语法需要解释的场景
例如 SQL 语法分析,金融中大量的运算等。不过这些部分也逐渐被专门工具所替代了。 - 数据分析工具、报表设计工具、科学计算工具等
五、注意事项
- 尽量不要找重要的模块使用解释器模式,否则维护会是一个很大的问题。可以选择使用
shell
、JRuby
、Groovy
等脚本语言来替代解释器模式。 - 对效率要求不高的场景(例如可以在晚间执行),因为解释器模式的性能并不高。
- 准备使用解释器模式时,可以考虑一下
Expression4J
、MESP
(Math Expression String Parser)、Jep
等开源的解析工具包。它们功能强大,且容易使用,效率也还不错,没必要自己从头开始编写(使用别人写好的工具也是一个很好的选择)。
查看更多:设计模式分类以及六大设计原则