解释器 Interpreter

解释器模式是一种行为设计模式,用于定义语言的语法^1规则表示,并提供解释器来处理句子中的语法。

解释器模式描述如何构建一个简单“语言”的语法解释器,通过更简洁的规则来表示复杂的逻辑。它的适用范围很小,只在一些非常特定的领域被用到(编译器、规则引擎、正则表达式、SQL 解析等)。


为什么要使用?

解释器模式的对象职责:

通过更简洁的规则来表示复杂的逻辑。

👉 将领域语言(即问题表征)定义为简单的语言语法。通过多个不同规则的简单组合来映射复杂的模型。

📜 例如,在开发中使用正则表达式来快速匹配 IP 地址,而不是将所有可能的情况都用 if-else 来进行编写。

👉 更便捷地提升解释数学公式这一类场景的计算效率。

📜 例如,需要通过一定的规则运算才能得出最后结果的情况,在使用解释器模式后,能够很好地通过预置地规则来进行判断和解释。


模式结构

  • 抽象表达式(Abstract Expression)定义一个解释器有哪些操作,可以是抽象类或接口。同时说明只要继承或实现的子节点都需要实现这些操作方法。
  • 终结符表达式(Terminal Expression)用于解释所有终结符^2的表达式。
  • 非终结符表达式(Nonterminal Expression)用于解释所有非终结符^3表达式。
  • 上下文(Context)包含解释器全局的信息。
  • 请求者(Client)调用终结符表达式和非终结符表达式来推导语法树。

解释器模式的类图:


模式实现

该示例使用解释器模式实现“判断两个字符串中至少存在一个是输入字符串的子串(或)”和“判断两个字符串是否同时为输入字符串的子串(且)”。在非终结符表达式中做“且”或“或”的操作;在终结符表达式中判断“终结符字符串”是否是“输入字符串”的子串。

示例程序的类图

代码实现

抽象表达式
1
2
3
4
5
6
7
8
9
/** 抽象表达式接口 */
public interface Expression {
/**
* 解释表达式
* @param con 表达式
* @return true满足条件
*/
boolean interpreter(String con);
}
终结符表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** 终结符 */
public class TerminalExpression implements Expression {
private String data;

public TerminalExpression(String data) {
this.data = data;
}

/**
* 解释表达式
* @param con 表达式
* @return true满足条件
*/
@Override
public boolean interpreter(String con) {
if(con.contains(data)) {
return true;
}
return false;
}
}
非终结符表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** “或”非终结符 */
public class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;

public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}

/**
* 解释表达式
* @param con 表达式
* @return true满足条件
*/
@Override
public boolean interpreter(String con) {
return expr1.interpreter(con) || expr2.interpreter(con);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** “且”非终结符 */
public class AndExpression implements Expression {
private Expression expr1;
private Expression expr2;

public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}

/**
* 解释表达式
* @param con 表达式
* @return true满足条件
*/
@Override
public boolean interpreter(String con) {
return expr1.interpreter(con) && expr2.interpreter(con);
}
}

代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** 测试解释器模式 */
public class Test {
public static void main(String[] args) {
// 创建两个终结符,生成它们的“或”非终结符
Expression mick = new TerminalExpression("mick");
TerminalExpression mia = new TerminalExpression("mia");
Expression isSingle = new OrExpression(mick, mia);
// 创建两个终结符,生成它们的“且”非终结符
Expression spike = new TerminalExpression("spike");
TerminalExpression mock = new TerminalExpression("mock");
Expression isCommitted = new AndExpression(spike, mock);
// 解释非终结符表达式
System.out.println("'mick'是否包含'mick'或'mia':" + isSingle.interpreter("mick"));
System.out.println("'mia'是否包含'mick'或'mia':" + isSingle.interpreter("mia"));
System.out.println("'max'是否包含'mick'或'mia':" + isSingle.interpreter("max"));

System.out.println("'mock-spike'是否同时包含'spike'和'mock':" + isCommitted.interpreter("mock-spike"));
System.out.println("'single-mock'是否同时包含'spike'和'mock':" + isCommitted.interpreter("single-mock"));
}
}

输出结果

1
2
3
4
5
'mick'是否包含'mick'或'mia':true
'mia'是否包含'mick'或'mia':true
'max'是否包含'mick'或'mia':false
'mock-spike'是否同时包含'spike'和'mock':true
'single-mock'是否同时包含'spike'和'mock':false

常用场景和解决方案

  • 语言的语法较为简单并且对执行效率要求不高。
  • 问题重复出现,且可以用一种简单的语言来进行表达。
  • 一个语言需要解释执行。

模式的优缺点

优点 缺点
很容易改变和扩展语法逻辑。 维护成本很高。
更容易实现语法。 执行效率较低。
应用场景单一,复用性不高。

拓展知识

  • 在解释器模式中通常会使用树的结构,终端表达式对象是叶对象,非终端表达式是组合对象。
  • 解释器模式能够通过一些简短的规则来解决复杂的数据匹配问题。


🔙 设计模式

📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言!

🔗参考文献:

▶️ bilibili-趣学设计模式;黄靖锋. –拉勾教育

📖 图解设计模式 /(日)结城浩著;杨文轩译. –北京:人民邮电出版社,2017.1