设计模式之解释器模式

解释器模式

解释器模式

解释器模式( Interpreter Pattern)是指给定一门语言,定义它的文法的一种表示,并定义个解释噐,该解释器使用该表示来解释语言中的句子,是一种按照规定的语法(文法)进行解析的模式,属于行为型模式。

就比如编译器可以将源码编译解释为机器码,让CPU能进行识别并运行。解释噐模式的作用其实与编译器一样,都是将一些固定的文法(即语法)进行解释,构建出一个解释句子的解释器。简单理解,解释器是一个简单语法分析工具,它可以识别句子语义,分离终结符号和非终结符号,提取出需要的信息,让我们能针对不同的信息做出相应的处理,其核心思想是识别文法,构建解释。

通用UML图

image-20210428234636883

从UML类图中,可以看到,解释器模式主要包含四种角色

抽象表达式( Expression):负责定义ー个解释方法 Interpret,交由具体子类进行具体解释

终结符表达式( TerminalExpression):实现文法中与终结符有关的解释操作。文法中的每个终结符都有一个具体终结表达式与之相对应,比如公式R=R1+R2,R1和R2就是终结符对应的解析R1和R2的解释器就是终结符表达式。通常一个解释器模式中只有一个终结符表达
式,但有多个实例,对应不同的终结符(R1,R2)

非终结符表达式( NonTerminalExpression):实现文法中与非终结符有关的解释操作。文法中的每条规则都对应于ー个非终结符表达式。非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,”+”就是非终结符,解析“+”的解释器就是一个非终结符
表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式

上下文环境类(Context):包含解释器之外的全局信息。它的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,给R1赋值100,给R2赋值200,这些信息需要存放到环境中。

解释器模式的应用场景

我们的程序中,如果存在一种特定类型的问题,该类型问题涉及多个不同实例,但是具备固定文法描述,那么可以使用解释噐模式对该类型问题进行解释,分离出需要的信息,根据获取的信息做出相应的处理。简而言之,对于ー些固定文法构建一个解释句子的解释器。解释器模式适用于以下应用场景:

1、一些重复出现的问题可以用一种简单的语言来进行表达

2、简单语法需要解释的场景

通用代码

抽象表达式( Expression)

1
2
3
4
public interface IExpression
{
String interpreter(Context context);
}

终结符表达式( TerminalExpression)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TerminalExpression implements IExpression
{
@Override
public String interpreter(Context context)
{
String[] strings = context.getExpression().split(" ");
int i = 0;
for(String s : strings)
{
if (NumberUtils.isDigits(s))
{
i ++;
}
}
return i + "个终结符";
}
}

非终结符表达式( NonTerminalExpression)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class NonTerminalExpression implements IExpression
{
@Override
public String interpreter(Context context)
{
String[] strings = context.getExpression().split(" ");
int i = 0;
for(String s : strings)
{
if (!NumberUtils.isDigits(s))
{
i ++;
}
}
return i + "个非终结符";
}
}

上下文环境类(Context)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Context
{
private String expression;

public String getExpression()
{
return expression;
}

public void setExpression(String expression)
{
this.expression = expression;
}
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main
{
public static void main(String[] args)
{
Context context = new Context();

context.setExpression("1 + 2 - 4");
TerminalExpression terminalExpression = new TerminalExpression();
System.out.println(terminalExpression.interpreter(context));
NonTerminalExpression nonTerminalExpression = new NonTerminalExpression();
System.out.println(nonTerminalExpression.interpreter(context));

}
}

输出

1
2
3个终结符
2个非终结符

解释器模式在源码中的体现

JDK源码中的Pattern对正则表达式的编译和解析。

Spring 的 SpelExpressionParser 和谷歌的aviator都是表达式引擎,应用了解释器模式。

解释器模式的优缺点

优点

1、扩展性强:在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需修改相应的非终结符表达式即可;若扩展语法时,只需添加相应非终结符类即可

2、増加了新的解释表达式的方式

3、易于实现文法:解释噐模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式。

缺点

1、语法规则较复杂时,会引起类膨胀,解释器模式每个语法都要产生一个非终结符表达式,当语法规则比较复杂时,就会产生大量的解释类,增加系统维护困难

2、执行效率比较低:解释器模式采用递归调用方法,每个非终结符表达式只夫心与自己有关的表达式,每个表达式需要知道最终的结果,因此完整表达式的最终结果是通过从后往前递归调用的方式获取得到。当完整表达式层级较深时,解释效率下降,且出错时调试困难,因为递归迭代层级太深。

打赏

请我喝杯咖啡吧~

支付宝
微信