github地址:https://github.com/1711680493
如需了解更多设计模式,请进入我的设计模式专栏
解释器模式
提供如何定义语言的文法,以及对语言句子的解释方法
文法指语言的语法规则,而句子是语言集中的元素
正则表达式等都是解释器模式
解释器模式是一种行为型模式
优点
扩展性好,由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法
容易实现,在语法树种的每个表达式节点类都是相似的,所以实现其文法较为容易
缺点
执行效率较低,解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,
且代码的调试过程也比较麻烦
会引起类膨胀,解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护
可应用的场景比较少,在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很难被用到
解释器模式的结构与组合模式相似,不过其包含的组成元素比组合模式多,
而且组合模式是对象结构型模式,而解释器模式是类行为型模式
结构
抽象表达式角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法interpret()
终结符表达式角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应
非终结者表达式角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结者表达式
环境角色:通常包含各个解释器需要的数据或是公共的功能,一般来传递所有解释器共享的数据,
后面的解释器可以从这里获取这些值
客户端:主要任务是需要将分析的句子或表达式转换成使用解释器对象描述的抽象语法树,
然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法
例子
我们通过此模式来解释一个加法
/**
* 解释器模式
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
public class InterpreterMode {
public static void main(String[] args) {
//创建环境
Context context = new Context();
//执行一段表达式
context.exec("1 + 1");
context.exec("2+1");
context.exec("1024+ 2048");
context.exec("1024 +1024");
}
}
/**
* 抽象表达式
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
interface Interpreter {
/**
* 表达式执行完后返回的值
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param interpreter 表达式
* @return
*/
int exec(String interpreter);
}
/**
* 加数 终结者
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class AddInterpreter implements Interpreter {
@Override
public int exec(String interpreter) {
//根据+分割 这里的\\是转义
String[] strs = interpreter.split("\\+");
if (strs.length > 1) {
//去空格 转 int 返回
return Integer.parseInt(strs[0].trim());
}
return 0;
}
}
/**
* 被加数 终结者
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class UnAddInterpreter implements Interpreter {
@Override
public int exec(String interpreter) {
//根据+分割
String[] strs = interpreter.split("\\+");
if (strs.length > 1) {
//去空格 转 int 返回
return Integer.parseInt(strs[1].trim());
}
return 0;
}
}
/**
* 运算符 终结者
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class SymbolInterpreter implements Interpreter {
@Override
public int exec(String interpreter) {
//运算符有很多,这里不区分加减乘除... 我们定义
//返回0则为+,1为-,2为*,3为/
if (interpreter.indexOf('+') != -1) {
return 0;
} else if (interpreter.indexOf('-') != -1) {
return 1;
} else if (interpreter.indexOf('*') != -1) {
return 2;
} else {
return 3;
}
}
}
/**
* 加法非终结者表达式 调用其他终结者表达式完成任务
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class AddNoInterpreter implements Interpreter {
private AddInterpreter add;
private SymbolInterpreter symbol;
private UnAddInterpreter unAdd;
public AddNoInterpreter(AddInterpreter add,SymbolInterpreter symbol,UnAddInterpreter unAdd) {
this.add = add;
this.symbol = symbol;
this.unAdd = unAdd;
}
@Override
public int exec(String interpreter) {
//判断符号是否为加号 是则解析加数和被加数 并执行操作
return symbol.exec(interpreter) == 0 ? add.exec(interpreter) + unAdd.exec(interpreter) : 0;
}
}
/**
* 环境类
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class Context {
//持有非终结者引用
private AddNoInterpreter ANI;
public Context() {
//初始化
ANI = new AddNoInterpreter(new AddInterpreter(), new SymbolInterpreter(), new UnAddInterpreter());
}
/**
* 执行操作
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
*/
public void exec(String add) {
System.out.println(add + '=' + ANI.exec(add));
}
}
应用场景
当语言的文法较为简单,且执行效率不是关键问题时
当问题重复出现,且可以用一种简单地语言来进行表达时
当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象的语法树的时候,如XML文档解释注意:解释器模式在实际的软件开发中使用比较少,因为会引起效率性能维护等问题,
在Java中可以用Expression4J或Jep来设计
扩展
在Java中,如果要对数表达式进行分析与计算,无须再用解释器模式进行设计了,Java提供了强大的数学公式解析器Expression4J,MESP,Jep等...
总结
思想:解释器模式就是用于解释一些东西
使用:根据模式结构,大致分工 (
客户端调用环境,环境持有非终结者并调用,非终结者持有 终结者/非终结者 并调用执行解释操作
)