sugar/ 十月 28, 2019/ python设计模式/ 0 comments

对每个应用来说,至少有以下两种不同的用户分类。

  • 基本用户:这类用户只希望能够凭直觉使用应用。他们不喜欢花太多时间配置或学习应 用的内部。对他们来说,基本的用法就足够了。
  • 高级用户:这些用户,实际上通常是少数,不介意花费额外的时间学习如何使用应用的 高级特性。如果知道学会之后能得到以下好处,他们甚至会去学习一种配置(或脚本) 语言。

解释器(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相关。

Share this Post

说点什么

avatar
  Subscribe  
提醒