解释器(Interpreter)模式

解释器模式是一种类行为型模式,用于给定语言的文法表示,并定义一个解释器来解释该语言中的句子。该模式适用于需要解释执行的语言,且文法简单的场景。模式包含抽象表达式、终结符表达式和非终结符表达式等参与者,通过它们协作完成对输入字符串的解释。在Java中,解释器模式常用于解析简单的语言或表达式,如正则表达式。
摘要由CSDN通过智能技术生成

解释器(Interpreter)模式

隶属类别——类行为型模式


1. 意图

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

2. 别名

3. 动机

如果一个特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构造一个解释器,该解释器通过解释这些句子来解决该问题。

例如,搜素匹配一个模式的字符串是一个常见问题。正则表达式是描述字符串模式的一个标准语言。与其为每一个的模式都构造一个特定的算法,不如使用一种通用的搜索算法来执行一个正则表达式,该正则表达式定义了待匹配字符串的集合。

解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。在下面的例子中,本设计模式描述了如何为正则表达式定义一个文法,如何表达一个特定的正则表达式,以及如何解释这个正则表达式。

考虑以下文法定义正则表达式:

expression ::= literal | alternation | sequence | repetition | '(' expression ')'
alternation ::= expression '|' expression
sequence ::= expression '&' expression
repetition ::= expression '*'
literal ::= 'a' | 'b' | 'c' | ... {'a' | 'b' | 'c' | ...}*

符号expression是开始符号,literal是定义简单字的终结符。

解释器模式使用类来表示每一条文法规则。在规则右边的符号是这些类的实例变量。上面的文法用五个类表示:一个抽象类RegularExpression和它四个子类LiteralExpression、AlternationExpression、SequenceExpression和RepetirionExpression后三个类定义的变量代表子表达式。

在这里插入图片描述

每个用这个文法定义的正则表达式都被表示为一个由这些类的实例构成的抽象语法树,例如,抽象语法树:

在这里插入图片描述

表示正则表达式:

raining & (dogs | cats) *

如果我们为RegularExpression的每一个子类都定义解释(Interpret)操作,那么就得到了为这些正则表达式的一个解释器。解释器将该表达式的上下文作为一个参数。上下文包含输入字符和关于面前它已有多少被匹配等信息。为匹配输入字符串的下一部分,每一个RegularExpression的子类都在当前上下文的基础上实现解释(interpret)操作,例如,

  • LiteralExpression将检查输入是否匹配它定义的字(literal)。
  • AlternationExpression将检查输入是否匹配它的任意一个选择项。
  • RepeatitionExpression将检查输入是否含有多个它所重复的表达式。
  • SequenceExpression将检查输入的表达式的顺序。

4. 适用性

当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好。

  • 文法很简单 对于复杂的文法,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间。
  • 效率不是一个关键问题 最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机,但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是可用的。

5. 结构

在这里插入图片描述

6. 参与者

  • AbstractExpression(抽象表达式,如RegularExpression)

    • 声明一个抽象的解释操作,这个接口被抽象语法树中所有的节点所共享。
  • TerminalExpression(终结符表达式, 如LiteralExpression)

    • 实现与文法中的终极符相关联的解释操作。
    • 一个句子中的每个终结符需要该类的一个实例。
  • NonterminalExpression(非终结符表达式, 如AlternationExpression, RepetitionExpression,SequenceExpression)

    • 对文法中的每一条规则R ::=R1R2…Rn都需要一个NonterminalExpression类。

    • 为从R1到Rn的每个字符都维护一个AbstractExpression类型的实例变量。

    • 为文法中的非终结符的实现解释(Interpret)操作,解释(Interpret)一般要递归地调用表示R1

      到Rn的那些对象的解释操作。

  • Context(上下文)

    • 包含解释器之外的一些全局信息。
  • Client(客户)

    • 构建(或被给定)表示该文法定义的语言中一个特地的句子的抽象语法树。该抽象语法树由NonterminalExpression和TerminalExpression的实例装配而成。
    • 调用解释操作

7. 协作

  • Client构建(或被给定)一个句子,它是有NonterminalExpression和TERMINALeXPRESSION的实例装配成的一个抽象语法树,然后初始化上下文并调用解释操作。
  • 每一飞终结符表达式节点定义相应子表达式的解释操作。而各终结符表达式的解释操作构成了递归的基础。
  • 每一节点的解释操作用上下文来存储和访问解释器的状态。

8. 效果

解释器模式有下列的优点

  • 1) 易于改变和扩展文法 因为该模式使用类来表达文法规则。你可以使用1继承来改变或扩展该文法。已有的表达式可被增量式的改变,而新的表达式可以定义为旧表达式的变体。
  • 2) 易于实现文法 定义抽象语法树种的各个节点的类的实现大体类似。这些类易于直接编写,通常他们也可用一个编译器或语法分析程序器自动生成。
  • 3) 增加了新的解释器表达式的方式 解释器模式使得实现新表达式"计算"变得容易。例如,你可以在表达式类上定义一个新的操作以支持优美打印或者表达式的类型检查。如果你经常创新的解释表达式的方式,那么可以考虑使用Visitor模式来避免修改这些代表文法的类。

缺点:

  • 1)复杂的文法难以维护 解释器模式为文法中的每一条规则至少定义一个类(使用BNF(巴科斯范式)定义的文法规则需要更多的类),因此包含许多规则的文法可能难以管理和文虎。可应用其他的设计模式来缓解这一问题。但当文法非常复杂是,其他的技术如语法分析程序或者编译器生成器更为合适。

9. 实现

Interpreter和Composite模式在实现的上有许多相同的地方。下面是Interpreter所要考虑的一些特殊问题:

  • 1)创建抽象语法树 解释器模式并未解释如何创建一个抽象的语法树。换言之,它不涉及语法分析。抽象语法树可用一个表驱动的语法分析树可用一个表驱动的语法分析程序来生成, 也可用手写的(通常为递归下降法)语法分析程序创建。或者直接由Client提供。

  • 2) 定义解释操作 并不一定会要在表达式类中定义解释操作。如果经常要创建一种新的解释器,那么使用Visitor模式将解释放入一个独立的"访问者"对象更好一些。例如,一个程序设计语言会有许多在抽象语法树上的操作,比如类型检查、优化、代码生成、等等。恰当的做法是使用一个访问者以避免在每一个类都定义这个操作。 how ?

    1. 与Flyweight模式共享终结符 在一个文法中,一个句子可能出现多个出现同一个终结符。此时最好共享那个符号的单个拷贝。计算机程序的文法是一个很好的例子——每个程序变量在整个代码中将会出现多次。在动机的例子中,一个句子中终结符dog(由LiteralExpression类描述)也可能多次。

    终结节点通常不存储关于它们在抽象语法树中位置的信息。在解释过程中,任何它们所需要的上下文信息都由父节点传递给它们。因此在共享的(状态)状态和传入的(外部的)状态区分得很明确,这就用到了Flyweight模式。

    例如,dog literalExpression的每一个实例接受一个包含目前已匹配子串信息的上下文。且每一个这样的LiteralExpression在它的解释操作中做同样一件事(它检查输入的下一部分是否包含一个dog)无论该实例出现在语法树的哪个位置。

10. 代码示例

本次想要设置的一个解释器模式时从一些String中解释一些pets的jump和bark动作。

首先是Context——PetContext.java

public class PetContext {
   
	public void barkPet(Pet pet) {
   
		pet.bark();;
	}
	
	public void jumpPet(Pet pet) {
   
		pet.jump();
	}
}

然后是AbstractExpression——Expression.java

public interface Expression {
   
	void interpret(PetContext pc);
}

两个TerMinalExpressin——BarkExpression.java & JumpExpression.java

BarkExpression.java

public class BarkExpression implements Expression {
   
	private Pet pet;
	
	BarkExpression(Pet pet) {
   
		this.pet = pet
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值