目录
- 1.概述
- 2.结构
- 3.案例实现
- 3.1.抽象表达式类
- 3.2.终结表达式
- 3.3.非终结表达式
- 3.4.环境类
- 3.5.测试
- 4.优缺点
- 5.使用场景
1.概述
(1)如下图,设计一个软件用来进行加减计算。我们第一想法可能就是使用工具类,提供对应的加法和减法的工具方法。
//用于两个整数相加
public static int add(int a,int b){
return a + b;
}
//用于两个整数相加
public static int add(int a,int b,int c){
return a + b + c;
}
//用于 n 个整数相加(使用可变参数)
public static int add(Integer... arr) {
int sum = 0;
for (Integer i : arr) {
sum += i;
}
return sum;
}
上面的形式比较单一、有限,如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算符与数值可以有无限种组合方式。比如 1 + 2 + 3 + 4 + 5、1 + 2 + 3 - 4等等。显然,现在需要一种翻译识别机器,能够解析由数字以及 +、- 符号构成的合法的运算序列。如果把运算符和数字都看作节点的话,能够逐个节点的进行读取解析运算,这就是解释器模式的思维。
(2)解释器模式 (Interpreter pattern) 是一种行为型设计模式,用于表示一个语言文法的语法规则,以及如何解析和执行该语法规则。解释器模式将一个语言表达式表示为对象,并定义了解析该表达式的语法规则。它包含了一个抽象表达式类和具体的表达式类。解释器模式主要通过使用递归来实现计算过程,即通过调用表达式对象的解释方法来进行计算。它可以用于编译器、解释器、数学公式计算等领域。
(3)在解释器模式中,我们需要将待解决的问题,提取出规则,抽象为一种“语言”。比如加减法运算,规则为:由数值和 +、- 符号组成的合法序列,“1 + 3 - 2” 就是这种语言的句子。解释器就是要解析出来语句的含义。但是如何描述规则呢?
(4)文法/语法规则(用于描述语言的语法结构的形式规则):
expression ::= value | plus | minus
plus ::= expression '+' expression
minus ::= expression '-' expression
value ::= integer
注意: 这里的符号“::=”表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法。上面规则描述为 :表达式可以是一个值,也可以是 plus 或者 minus 运算,而 plus 和 minus 又是由表达式结合运算符构成,值的类型为整型数。
(5)抽象语法树
在计算机科学中,抽象语法树 (Abstract Syntax Tree,AST),或简称语法树 (Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。用树形来表示符合文法规则的句子:
2.结构
解释器模式包含以下主要角色:
- 抽象表达式 (Abstract Expression) 角色:定义了一个抽象的接口,用于解析语法规则。通常它是一个抽象类或接口,其中包含了解释器需要实现的方法。。
- 终结符表达式 (Terminal Expression) 角色:终结符表达式是最基本的表达式,它代表语言中的一个基本单元,通常是一个具体的值或一个变量。
- 非终结符表达式 (Nonterminal Expression) 角色:非终结符表达式是由多个终结符表达式或其他非终结符表达式组成的复合表达式。
- 环境 (Context) 角色:环境对象包含了解析器运行所需要的全局信息。
- 客户端 (Client) 角色:客户端用于创建解释器并调用其解释方法来解析语法规则。
3.案例实现
【例】设计实现加减法的软件
具体实现代码如下:
3.1.抽象表达式类
AbstractExpression.java
//抽象表达式类
public abstract class AbstractExpression {
public abstract int interpret(Context context);
}
3.2.终结表达式
Variable.java
//用于封装变量的类
public class Variable extends AbstractExpression{
//声明存储变量名的成员变量
private String name;
public Variable(String name) {
this.name = name;
}
@Override
public int interpret(Context context) {
//直接返回变量的值
return context.getValue(this);
}
@Override
public String toString() {
return name;
}
}
3.3.非终结表达式
Plus.java
//加法表达式类
public class Plus extends AbstractExpression{
// + 左边的表达式
private AbstractExpression left;
// + 右边的表达式
private AbstractExpression right;
public Plus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
//将左边表达式的结果和右边的进行相加
return left.interpret(context) + right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + "+" + right.toString() + ")";
}
}
Minus.java
//减法表达式类
public class Minus extends AbstractExpression{
// - 左边的表达式
private AbstractExpression left;
// - 右边的表达式
private AbstractExpression right;
public Minus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
//将左边表达式的结果和右边的进行相减
return left.interpret(context) - right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + "-" + right.toString() + ")";
}
}
3.4.环境类
Context.java
//环境角色类
public class Context {
//定义一个 map 集合,用来存储变量以及对应的值
private Map<Variable,Integer> map = new HashMap<>();
//添加变量的功能
public void assign(Variable var, Integer value){
map.put(var, value);
}
//根据变量获取对应的值
public int getValue(Variable var){
return map.get(var);
}
}
3.5.测试
Client.java
public class Client {
public static void main(String[] args){
//创建环境对象
Context context = new Context();
//创建多个变量对象
Variable a = new Variable("a");
Variable b = new Variable("b");
Variable c = new Variable("c");
Variable d = new Variable("d");
//将变量存储到环境变量中
context.assign(a, 1);
context.assign(b, 2);
context.assign(c, 3);
context.assign(d, 4);
//获取抽象语法树 a + b - c + d
AbstractExpression expression = new Minus(a, new Plus(new Minus(b, c), d));
//解释,即计算
int result = expression.interpret(context);
System.out.println(expression + "=" + result);
}
}
结果如下:
(a-((b-c)+d))=-2
4.优缺点
(1)解释器模式的主要优点是:
- 灵活性:解释器模式可以根据需要动态地修改解释器的表达式,从而扩展或修改语言。
- 易于扩展:新的解释器可以通过扩展抽象语法树节点类来轻松添加到系统中。
- 可重用性:同样的解释器可以在不同的环境下重用,只需要修改解释器的上下文即可。
- 独立性:解释器模式使得解释器与其它部分相互独立,修改解释器不会影响其他部分的功能。
(2)解释器模式的主要缺点是:
- 复杂性:因为解释器模式涉及到抽象语法树、终结符和非终结符等概念,因此实现起来比较复杂。
- 效率问题:由于解释器模式使用递归调用的方式进行解释处理,因此对于复杂的语法和大量的数据处理,可能会导致效率问题。
5.使用场景
(1)解释器模式适用于以下场景:
- 当有一个简单的语法规则,并且需要频繁地对该语法进行解释和执行时,可以考虑使用解释器模式。
- 当需要将一个语言或规则进行扩展或修改时,解释器模式可以提供灵活的扩展性和易于修改的特性。
- 当语法规则相对稳定,但需要根据不同的上下文进行不同的解释时,可以使用解释器模式。
- 当需要解耦语法规则的解释过程与具体的操作时,解释器模式可以将语法解释与操作分离,从而提高代码的可维护性和可扩展性。
- 当需要构建一个能够解释执行特定领域语言的工具或引擎时,解释器模式是一种常用的设计模式。
(2)总之,解释器模式适用于需要解释和执行简单语法规则、动态扩展语言等场景,可以提供灵活性、可扩展性和可维护性。