一、定义
给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。
解释器是一种用的比较少的行为模式,其提供了一种解释语言的语法,或者表达式的方式。该模式定义了一个表达式的接口。
二、实际运用
- 正则表达式,比如,判断是否是手机号的正则:
"^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"
- xml解析
- sql语句的执行
三、重点
3.1 必须有一个抽象接口
所有的具体表达式的父接口,接口中主要有一个interpret()方法,称为解释操作。由各个实现类来完成具体的解释任务。
3.2 构建语法树
几种角色构建起来的语法树。
四、角色
- 终结符:语言中用到的基本元素,一般不能再被分解,如: x -> xa, 这里a是终结符,因为没有别的规则可以把a变成别的符号,不过x可以变成别的符号,所以x是非终结符。
- 终结符表达式角色:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例对应不同的终结符。
- 非终结符表达式角色:文法中的每条规则对应于一个非终结表达式, 非终结表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
- 抽象表达式:声明一个抽象的解释操作,这个接口为所有具体表达式角色都要实现的。
- 环境角色:包含解释器之外的一些全局信息。
这几个角色构成的类图如下:
五、实现一个解释器代码,解释:3 * 4 * 5 / 5 % 7,这个表达式
5.1 创建抽象表达式
/**
* 抽象表达式角色
*
* Created by Administrator on 2017/10/25.
*/
public interface NodeInterface {
public int interpreter();
}
5.2 创建终结符表达式
解释方法直接返回当前终结符的值
/**
* 终结符表达式
*
* Created by Administrator on 2017/10/25.
*/
public class ValueNode implements NodeInterface {
private int value;
public ValueNode(int value) {
this.value = value;
}
@Override
public int interpreter() {
return value;
}
}
5.3 创建抽象的非终结符表达式
因为每个非终结符表达式都是两个终结符之间的运算,所以先抽象一下
/**
* 非终结符抽象类
*
* Created by Administrator on 2017/10/25.
*/
public abstract class SymbolNode implements NodeInterface {
protected NodeInterface left;
protected NodeInterface right;
public SymbolNode(NodeInterface left, NodeInterface right) {
this.left = left;
this.right = right;
}
}
5.4 创建非终结符表达式
乘法、除法、取余都类似,只列一个
/**
* 非终结符表达式:乘法
*
* Created by Administrator on 2017/10/25.
*/
public class MultiNode extends SymbolNode {
public MultiNode(NodeInterface left, NodeInterface right) {
super(left, right);
}
@Override
public int interpreter() {
return left.interpreter()*right.interpreter();
}
}
5.5 构建语法树
/**
* 表达式构建起来的语法树
*
* Created by Administrator on 2017/10/26.
*/
public class Calculate {
public int build(String content){
NodeInterface left = null;
NodeInterface right = null;
NodeInterface lastNode = null;
String[] strs = content.split(" ");
for (int i = 0;i<strs.length;i++){
if ("*".equals(strs[i])){
//乘法
left = lastNode;
right = new ValueNode(Integer.parseInt(strs[++i]));
lastNode = new MultiNode(left,right);
}else if ("/".equals(strs[i])){
//除法
left = lastNode;
right = new ValueNode(Integer.parseInt(strs[++i]));
lastNode = new DviNode(left,right);
}else if ("%".equals(strs[i])){
//求余
left = lastNode;
right = new ValueNode(Integer.parseInt(strs[++i]));
lastNode = new ModNode(left,right);
}else{
lastNode = new ValueNode(Integer.parseInt(strs[i]));
}
}
return lastNode.interpreter();
}
}
5.6 调用解释方法解释固定语法
String str = "3 * 4 * 5 / 5 % 7 * 10";
Calculate calculate = new Calculate();
int result = calculate.build(str);
Log.e("MainActivity","result:"+result);
结果为:
10-25 12:12:28.913 3887-3887/com.interpreterpattern E/MainActivity: result:50
六、应用场景
- 简单的语言需要解释执行而且可以将该语言中的语句表示一个抽象的语法树
- 对于某个特定的领域出现的不断重复的问题,可以转换成一种语法规则下的语句
七、优缺点
优点:解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。
缺点:
- 解释器模式会引起类膨胀,每个语法都要产生一个非终结符表达式,语法规则比较复杂时,可能产生大量的类文件,难以维护。
- 解释器模式采用递归调用方法,它导致调试非常复杂。
- 解释器由于使用了大量的循环和递归,所以当用于解析复杂、冗长的语法时,效率是难以忍受的。