背景
在软件开发中,可能会出现某些相似的功能多次出现,这些功能有一定的相似性与规律性。这是我们就可以将其归纳成一种简单的语言。这就是解释器模式的来源。
上面的讲述是不是云里雾里的,那我们来举个例子。我们要开发一个功能,要求输入公式,输入参数,返回正确结果。例如我们输入参数:
代码语言:javascript复制a=2;b=4;c=10
我们再输入表达式:
代码语言:javascript复制a b-c
输出结果:-4。
我们再来输入一个表达式:
代码语言:javascript复制a * b / c
输出结果:0.8。
如果要你开发这个功能,你会怎么做?如果使用解释器模式,会有很好的效果。
什么是解释器模式
“Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.(给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。) ”
解释器模式主要有下面5个要素组成:
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
结构图如下:
解释器模式
代码实现
我们来看看代码如何实现:
Context
代码语言:javascript复制public class Context {
private AbstractExpression exp;
public Context() {
//数据初始化
}
public void operation(String info) {
//调用相关表达式类的解释方法
}
}
AbstractExpression
代码语言:javascript复制public interface AbstractExpression {
Object interpret(String info); //解释方法
}
TerminalExpression
代码语言:javascript复制public class TerminalExpression implements AbstractExpression{
@Override
public Object interpret(String info) {
System.out.println("进入终结符表达式的处理逻辑");
return null;
}
}
NonterminalExpression
代码语言:javascript复制public class NonterminalExpression implements AbstractExpression {
private AbstractExpression exp1;
private AbstractExpression exp2;
@Override
public Object interpret(String info) {
System.out.println("进入非终结表达式的逻辑");
return null;
}
}
由于解释器模式是一个比较少用的模式。上面的模板已经给出,大家可以思考下这个模式怎么使用。
关于解释器模式的思考
解释器模式往往有效率问题,会增加维护的复杂度。像文章开头的问题,我们可以用Java中的数学解析器公式Expression4J、MESP(Math Expression String Parser) 和 Jep 等。可以解释一些复杂的表达式。使用比原始的解释器模式更简单。