代码链接
koonchen/design-patterns/interpreter
要点
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
例如现在有一个正则表达式,其中有以下各个符号:
其中,符号 expression
是开始符号,literal
是字的终结符。
解释器使用类来表示每一条文法,它的右边的符号是实例,看上图,根据这些,我们需要五个语法类:
假设现在有一个正则表达:
1
| raining & (dogs | cats) *
|
它对应的抽象语法树是:
解释器模式的适用性:
- 文法的类层次庞大而且无法管理时。
- 效率不是一个关键问题,最搞笑的解释器通常不是通过直接解释语法分析树实现的,而是转换成另一个形式,这种情况下,解释器仍可以用解释器模式来实现。
解释器模式的参与者:
- AbstractExpression — 声明一个抽象的解释操作,这个接口为抽象语法树中所有节点所共享
- TerminalExpression — 实现与文法中的终结符相关联的解释操作
- NonterminalExpresson — 对每一条规则进行解释操作,一般用递归表示
- Context — 包含解释器之外的一些全局信息
- Client — 调用解释操作
解释器模式的结构:
解释器模式的优缺点:
- 易于改变和扩展文法
- 易于实现文法
- 增加新的解释器表达的方式
- 复杂的文法难以维护
实现
在实现前有一些需要考虑的问题:
- 抽象语法树:解释器模式并未监视如何创建一个抽象的语法树
- 定义解释操作:并不一定要在表达式中定义解释操作
- 与
Flyweight
模式共享终结符:一个句子中可能出现多次同一个终结符,最好共享这个符号的单个拷贝,这个节点通常不存储关于它在语法树中的位置信息,任何它们需要的信息都是父节点传递的,存在内部状态和外部状态,所以需要使用 Flyweight
。
Java 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import java.util.ArrayList; import java.util.List;
class Context { private String name = "Context";
public String getName() { return name; } }
interface AbstractExpression { void interpret(Context context); }
class TerminalExpression implements AbstractExpression {
@Override public void interpret(Context context) { System.out.println("TerminalExpression instance has been created"); } }
class NonterminalExpression implements AbstractExpression {
private List<AbstractExpression> list = null; private String name;
public NonterminalExpression(String name) { this.name = name; }
public void setList(List<AbstractExpression> list) { this.list = new ArrayList<>(); for (AbstractExpression expression: list) { this.list.add(expression); } }
@Override public void interpret(Context context) { if (this.list != null) { for (AbstractExpression expression: this.list) { expression.interpret(context); } } System.out.println(this.name + " instance has been created"); } }
public class InterpreterClient { public static void main(String[] args) { Context context = new Context(); AbstractExpression aRepetition = new NonterminalExpression("repetition"); AbstractExpression dogsLiteral = new NonterminalExpression("dogs"); AbstractExpression catsLiteral = new NonterminalExpression("cats"); List<AbstractExpression> list = new ArrayList<>(); list.add(dogsLiteral); list.add(catsLiteral); ((NonterminalExpression) aRepetition).setList(list); aRepetition.interpret(context); } }
|
这里的 NonterminalExpression
实现一个重复表达式的操作,而 Context
和 TerminalExpression
并没有实际作用,但还是能看出这个模式的合理性。
相关模式
- Composite 模式:抽象语法树是一个复合模式的实例
- Flyweight 模式:说明了如何在抽象语法树中共享终结符
- Iterator 模式:解释器可用一个迭代器遍历该结构
- Visitor 模式:可用来在一个类中维护抽象语法树中各个节点的行为