解释器模式
对每个应用来说,至少有一下两种不同的用户分类。
基本用户:基本用法就够了
高级用户:少数,会花费额外时间如何使用应用的特性。如果知道学会以后能得到以下好处,他们甚至会去学习一种配置语言。
- 能够更高的控制一个应用
- 以更好地方式表达想法
- 提高生产力
解释器模式仅能引起应用的高级用户的兴趣。这是因为解释器模式背后的主要思想是让非初级用户和领域专家使用一门简单的语言来表达想法。
然而,什么是一种简单的语言,对于我们的需求来说,一种简单的语言就是没编程语言那么复杂的语言
一般而言,我们要创建的是一种领域特定语言(Domain Specific Language, DSL).DSL是一种针对一个特定领域的有限表达能力的计算机语言。很多不同的事情都是用DSL,比如战斗模拟、记账、可视化、配置、通信协议等
DSL分为内部DSL、外部DSL
1、内部DSL构建在一种宿主编程语言之上。内部DSL的一个例子是,使用python解决线性方程组的一种语言。使用内部DSL的优势是我们不必担心创建、编译、及解析语法,
因为这些已经被宿主语言解决掉了。劣势是受限于宿主语言的特性。如果宿主语言不具备这些特性,构建一种表达能力强、简洁而且优美的内部DSL是富有挑战性的
2、外部DSL不依赖某种某种语言。DSL的创建者可以决定语言的方方面面,但也要负责为其创建一个解释器和编译器。为一种新语言创建解释器和编译器是一个非常复杂、长期而又痛苦的过程
现实中的例子:演奏者-->五线谱-->音乐
解释器模式仅与内部DSL相关,因此我们的目标是使用宿主机语言提供的特性构建一种简单但有用的语言,在这里,宿主语言是python.
注意:解释器根本不处理语言解析他假设我们已经有某种便利形式的解析好的数据,可以是抽象语法树或其他好用的数据结构
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()