第27章 其实你不懂老板的心——解释器模式
定义
给定一个语言,定义它的文法表示,并定义一个解释器。解释器使用文法表示来解释语言中的句子。
应用场景
如果一个特定类型的问题发生的频率足够高(比如翻译),那么就将该问题理解成一种“语言”,问题的实例理解成一个简单语言中的句子。这样就可以构建一个解释器,通过解释句子来解决该问题。简单说来,事先定义好文法(需要翻译的内容,包含哪些语法,每一个语法对应一个类),然后翻译的时候,根据传入的待翻译语句,创建对应的文法中的对象,最终形成文法表达的语句,然后执行翻译。
使用这个模式之后,问题就便于扩展,如果句子增添了其它语法,定义相应的文法类就行。如果不用这个模式,那么每一个句子都要写一个程序来翻译,句子变了翻译程序要变,很麻烦。
还有一个例子,就是搜索,正则表达式就是文法,正则表达式引擎就是解释器,在某个文章中搜索特定的字符串就是要解决的问题。浏览器解析HTML,编译器生成程序,都包含这个解释器的成分。
实现
- AbstractExpression: 文法表达式抽象类,包含一个抽象的解释接口(Interpret(Context)), 期内的各类文法构成都会调用该接口。
- TerminalExpression: 终结符表达式,实现文法中终结符相关的解释操作。
- NoterminalExpression: 非终结符表达式,实现文法中非终结符相关的解释操作,对应文法中的每一条语法规则。
- Context: 解释器之外的信息,比如需要使用解释器处理的内容。
- Client: 获取待处理问题内容(Context),根据内容组成创建表达它的文法树(AbstractExpression),再依次解释文法树节点(Interpret)以解决该问题。
可参考BNF范式来理解这里的终结符和非终结符。客户端可能会结合如工厂模式等方式,增加修改的灵活度。
实现类图:
使用图:
其实,关键就三点:
- 定义可能的文法符号,以期能尽可能多的表达待解决的问题的数量,不同文法符号有不同的处理动作(例如正则表达式的源字符,以及每个元字符匹配时的返回动作)。
- 给出一个具体问题,用前面定义的文法符号,构造该具体问题对应的文法树(例如被搜索的字符串,将该字符串表达成需要输入的正则表达式)。
- 调用每一个节点并解释执行(根据文法符号的动作,对输入正则表达式执行搜索)。
特点
- 优点:可以很容易的扩展和改变文法,进而提升解决问题的能力。
- 缺点: 每一个文法规则都需要定义一个类,文法复杂时类会很多难以维护和管理,这个时候可以使用其它技术比如语法分析程序或者编译生成器来处理。
总之,解释器模式其实就是用一个迷你语言表述问题,用迷你程序描述待解决问题。语言需要解释执行并且能够抽象成语法树的时候,可使用解释器模式。