对每个应用来说,至少有以下两种不同的用户分类。
- 基本用户:这类用户只希望能够凭直觉使用应用。他们不喜欢花太多时间配置或学习应 用的内部。对他们来说,基本的用法就足够了。
- 高级用户:这些用户,实际上通常是少数,不介意花费额外的时间学习如何使用应用的 高级特性。如果知道学会之后能得到以下好处,他们甚至会去学习一种配置(或脚本) 语言。
解释器(Interpreter)模式仅能引起应用的高级用户的兴趣。这是因为解释器模式背后的主 要思想是让非初级用户和领域专家使用一门简单的语言来表达想法。
一般而言,我们想要创建的是一种领域特定语言(Domain Specific Language,DSL)。DSL 是一种针对一个特定领域的有限表达能力的计算机语言。很多不同的事情都使用DSL,比如,战 斗模拟、记账、可视化、配置、通信协议等。DSL分为内部DSL和外部DSL。
内部DSL构建在一种宿主编程语言之上。内部DSL的一个例子是,使用Python解决线性方程 组的一种语言。使用内部DSL的优势是我们不必担心创建、编译及解析语法,因为这些已经被宿 主语言解决掉了。劣势是会受限于宿主语言的特性。如果宿主语言不具备这些特性,构建一种表 达能力强、简洁而且优美的内部DSL是富有挑战性的。
外部DSL不依赖某种宿主语言。DSL的创建者可以决定语言的方方面面(语法、句法等) , 但也要负责为其创建一个解析器和编译器。为一种新语言创建解析器和编译器是一个非常复杂、 长期而又痛苦的过程。
解释器模式仅与内部DSL相关。因此,我们的目标是使用宿主语言提供的特性构建一种简单 但有用的语言,在这里,宿主语言是Python。注意,解释器根本不处理语言解析,它假设我们已 经有某种便利形式的解析好的数据,可以是抽象语法树(abstract syntax tree,AST)或任何其他 好用的数据结构。
现实生活的例子
音乐演奏者是现实中解释器模式的一个例子。五线谱图形化地表现了声音的音调和持续时 间。音乐演奏者能根据五线谱的符号精确地重现声音。在某种意义上,五线谱是音乐的语言,音 乐演奏者是这种语言的解释器。下图展示了音乐例子的图形化描述。
实现
# coding: utf-8 from pyparsing import Word, OneOrMore, Optional, Group, Suppress, alphanums class Gate: def __init__(self): self.is_open = False def __str__(self): return 'open' if self.is_open else 'closed' def open(self): print('opening the gate') self.is_open = True def close(self): print('closing the gate') self.is_open = False class Garage: def __init__(self): self.is_open = False def __str__(self): return 'open' if self.is_open else 'closed' def open(self): print('opening the garage') self.is_open = True def close(self): print('closing the garage') self.is_open = False class Aircondition: def __init__(self): self.is_on = False def __str__(self): return 'on' if self.is_on else 'off' def turn_on(self): print('turning on the aircondition') self.is_on = True def turn_off(self): print('turning off the aircondition') self.is_on = False class Heating: def __init__(self): self.is_on = False def __str__(self): return 'on' if self.is_on else 'off' def turn_on(self): print('turning on the heating') self.is_on = True def turn_off(self): print('turning off the heating') self.is_on = False class Boiler: def __init__(self): self.temperature = 83 # in celsius def __str__(self): return 'boiler temperature: {}'.format(self.temperature) def increase_temperature(self, amount): print("increasing the boiler's temperature by {} degrees".format(amount)) self.temperature += amount def decrease_temperature(self, amount): print("decreasing the boiler's temperature by {} degrees".format(amount)) self.temperature -= amount class Fridge: def __init__(self): self.temperature = 2 # 单位为摄氏度 def __str__(self): return 'fridge temperature: {}'.format(self.temperature) def increase_temperature(self, amount): print("increasing the fridge's temperature by {} degrees".format(amount)) self.temperature += amount def decrease_temperature(self, amount): print("decreasing the fridge's temperature by {} degrees".format(amount)) self.temperature -= amount def main(): word = Word(alphanums) command = Group(OneOrMore(word)) token = Suppress("->") device = Group(OneOrMore(word)) argument = Group(OneOrMore(word)) event = command + token + device + Optional(token + argument) gate = Gate() garage = Garage() airco = Aircondition() heating = Heating() boiler = Boiler() fridge = Fridge() tests = ('open -> gate', 'close -> garage', 'turn on -> aircondition', 'turn off -> heating', 'increase -> boiler temperature -> 5 degrees', 'decrease -> fridge temperature -> 2 degrees') open_actions = {'gate': gate.open, 'garage': garage.open, 'aircondition': airco.turn_on, 'heating': heating.turn_on, 'boiler temperature': boiler.increase_temperature, 'fridge temperature': fridge.increase_temperature} close_actions = {'gate': gate.close, 'garage': garage.close, 'aircondition': airco.turn_off, 'heating': heating.turn_off, 'boiler temperature': boiler.decrease_temperature, 'fridge temperature': fridge.decrease_temperature} for t in tests: if len(event.parseString(t)) == 2: # 没有参数 cmd, dev = event.parseString(t) cmd_str, dev_str = ' '.join(cmd), ' '.join(dev) if 'open' in cmd_str or 'turn on' in cmd_str: open_actions[dev_str]() elif 'close' in cmd_str or 'turn off' in cmd_str: close_actions[dev_str]() elif len(event.parseString(t)) == 3: # 有参数 cmd, dev, arg = event.parseString(t) cmd_str, dev_str, arg_str = ' '.join(cmd), ' '.join(dev), ' '.join(arg) num_arg = 0 try: num_arg = int(arg_str.split()[0]) # 抽取数值部分 except ValueError as err: print("expected number but got: '{}'".format(arg_str[0])) if 'increase' in cmd_str and num_arg > 0: open_actions[dev_str](num_arg) elif 'decrease' in cmd_str and num_arg > 0: close_actions[dev_str](num_arg) if __name__ == '__main__': main()
解释器模式用于为高级用户和领域专家提供一个类编 程的框架,但没有暴露出编程语言那样的复杂性。这是通过实现一个DSL来达到目的的。
DSL是一种针对特定领域、表达能力有限的计算机语言。DSL有两类,分别是内部DSL和外 部DSL。内部DSL构建在一种宿主编程语言之上,依赖宿主编程语言,外部DSL则是从头实现, 不依赖某种已有的编程语言。解释器模式仅与内部DSL相关。
说点什么