介绍
解释器模式是类的行为模式。给定一个语言之后,解释器模式定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。
模式引入
如果某一类问题一再地发生的话,而且有一定的相似性和规律性。那么一个有意义的做法就是将此类问题的各个实例表达为一个简单语言中的语句。这样就可以创建一个解释器,通过解释这些语句达到解决问题的目的。例如:正则表达式、SQL、运算表达式计算等。
模式角色
解释器模式有以下角色:
- Expression(抽象表达式),定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- TerminalExpression(终结符表达式),是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- NonterminalExpression(非终结符表达式),也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- Context(环境),提供解释器之外的一些数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- Client(客户端),主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
模式结构图
模式实现
假设:武汉市最近出了新政策(我瞎说的,只是演示程序),武汉市的老人、孕妇、儿童乘车免费:
- 武汉市老人、孕妇、儿童以外的人乘车收费;
- 非武汉市内人乘车收费。
如何判断哪个城市的哪些人乘车免费?假设能收集到这些信息:“武汉市的老人”、“黄冈市的老人”、“武汉市的年轻人”、“荆州市的老人”…
在前面的“模式引入里”描述过:如果某一类问题一再地发生的话,而且有一定的相似性和规律性。那么一个有意义的做法就是将此类问题的各个实例表达为一个简单语言中的语句。这样就可以创建一个解释器,通过解释这些语句达到解决问题的目的。
文法分析
Expression ::= City | Person | And
And ::= Expression ‘AND’ Expression
City ::= ‘武汉’
Person ::= ‘老人’|‘孕妇’|‘儿童’
代码实现
/**
* 武汉市最近出了新政策(我瞎说的,只是演示程序),武汉市的老人、孕妇、儿童乘车免费:
* 1:武汉市老人、孕妇、儿童以外的人乘车收费;
* 2:非武汉市内人乘车收费。
* <p>
* 如何判断哪个城市的哪些人乘车免费?
* 假设能收集到这些信息:“武汉市的老人”、“黄冈市的老人”、“武汉市的年轻人”、“荆州市的老人”.....
* <p>
* 那么就可以用解释器模式实现如下:
* 首先分析出文法如下:
* Expression ::= City | Person | And
* And ::= Expression 'AND' Expression
* City ::= ‘武汉’
* Person ::= ‘老人’|‘孕妇’|‘儿童’
*/
interface Expression {
boolean interpret(InterpreterContext ctx);
}
/**
* 非终结符表达式
*/
class AndExpression implements Expression {
private Expression city;
private Expression person;
public AndExpression(Expression city, Expression person) {
this.city = city;
this.person = person;
}
@Override
public boolean interpret(InterpreterContext ctx) {
return city.interpret(ctx) && person.interpret(ctx);
}
}
/**
* 终结符表达式-城市
* 武汉、黄冈、荆州...
*/
class City implements Expression {
private String name;
public City(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public boolean interpret(InterpreterContext ctx) {
return ctx.lookup(name);
}
}
/**
* 终结符表达式-人群
* 老人、孕妇、儿童、年轻人...
*/
class Person implements Expression {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public boolean interpret(InterpreterContext ctx) {
return ctx.lookup(name);
}
}
/**
* 环境
*/
class InterpreterContext {
private HashMap<String, Boolean> map = new HashMap<>();
public void assign(String info) {
assign(info, true);
}
public void assign(String info, boolean legal) {
map.put(info, legal);
}
public boolean lookup(String info) {
Boolean result = map.get(info);
if (null == result) {
return false;
}
return result;
}
}
/**
* Client
*/
class InterpreterClient {
public static void main(String[] args) {
InterpreterContext bus = new InterpreterContext();
bus.assign("武汉");
bus.assign("老人");
bus.assign("孕妇");
bus.assign("儿童");
Expression e = null;
City city = null;
Person person = null;
city = new City("黄冈");
person = new Person("老人");
e = new AndExpression(city, person);
print(city.getName(), person.getName(), e.interpret(bus));
city = new City("武汉");
person = new Person("老人");
e = new AndExpression(city, person);
print(city.getName(), person.getName(), e.interpret(bus));
// 打印结果如下:
// 黄冈的老人乘车收费
// 武汉的老人乘车免费
}
public static void print(String city, String person, boolean result) {
if (result) {
System.out.println(city + "的" + person + "乘车免费");
} else {
System.out.println(city + "的" + person + "乘车收费");
}
}
}
模式应用场景
1:当语言的文法较为简单,且执行效率不是关键问题时。
2:当问题重复出现,且可以用一种简单的语言来进行表达时。
3:当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。
注意:解释器模式是一种不常用的设计模式,在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。
模式优缺点
优点:
- 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。:
缺点:
- 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
- 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
- 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
参考
https://www.cnblogs.com/cbf4life/archive/2009/12/17/1626125.html
https://zhuanlan.zhihu.com/p/86425330?from_voters_page=true
https://www.cnblogs.com/zyrblog/p/9253970.html
http://m.biancheng.net/view/1402.html