解释器模式(Interpreter)及代码实现

模式定义:

将用户 输入的 具有一定规则的 语句 通过 解释器转译为 能够用代码执行; 就是将 一种语言 解释成另一种语言;

DSL(Domain Specific Language):针对一个特定领域的有限表达能力的计算机语言;   如下概念引用自 <<精通Python设计模式>> 书籍

DSL分为内部DSL和外部DSL。
内部DSL构建在一种宿主编程语言之上。内部DSL的一个例子是,使用Python解决线性方程 组的一种语言。使用内部DSL的优势是我们不必担心创建、编译及解析语法,因为这些已经被宿 主语言解决掉了。劣势是会受限于宿主语言的特性。如果宿主语言不具备这些特性,构建一种表 达能力强、简洁而且优美的内部DSL是富有挑战性的。
外部DSL不依赖某种宿主语言。DSL的创建者可以决定语言的方方面面(语法、句法等),但也要负责为其创建一个解析器和编译器。为一种新语言创建解析器和编译器是一个非常复杂、长期而又痛苦的过程。
解释器模式仅与内部DSL相关。

内部DSL  是让我们 通过 在 宿主语言(此处为python语言)提供的各种特性 基础上 创建一种简单,有规则,方便用户使用的 新语言; 而解释器模式 就是 将 用户 根据 新语言 特性 编写的语句 翻译成 python能运行的代码;

如: 小学老是 在 判断乘法 "1 x 2 = 2" 是否正确时 乘法符号 x, 等于号 = 就是 一种新语言特性,经过 解释器模式 翻译成 python代码 就是 *,运行结果就是 "1 * 2 == 2"  结果为True表示 小学生答题正确;

 

生活中的例子

  • markdown 标记语言: 用户 输入 不同符号,显示不同的 样式;
  • 语言翻译: 如 从 英语 经 翻译官 翻译成 汉语;
  • 乐谱: 乐谱 经 演奏家 演奏出 动听的音乐;
  • 智能家居: 用户对着空气 说 把 卧室灯打开; 人工智能 识别 文字后,将 文字 匹配为 对应的 可执行命令,然后 执行对应命令 开灯;

 

代码中的例子(何时该使用此模式):

  • sql语句;  这种数据库 语句 便是 数据库 提供给用户使用的 一种特性语言, 用户输入语句后,经 数据库 进行 语句合法性校验 然后 转换为 对应执行命令,操作完数据后 将结果返回给用户;
  • 编译器: 将源代码翻译成机器能认识的机器码;

 

该模式的主要优缺点如下:

优点:

  • 针对特定人群 在不考虑性能的情况下,将 一些功能 构造成一种 语言;方便 特定人群的快速 熟悉,使用 对应功能;

缺点:

  • 实际应用场景非常少, 很少有需求 要 实现 某一种特性语言,更别说 用此模式 进行解析成可运行的代码;
  • 效率低下:  特性语言 通过 运行解释器代码 解析成 可运行代码 便耗费了大量时间(此模式的用处决定了自身的效率低下);并且 在解释器 解析 用户语句的过程中 需要对 字符串进行各种匹配处理,在 语言规则 复杂的情况中,必然要 进行 大量的 规则匹配,这时必然增加其 运行时间;

和 其他模式 的 比较:

  • 命令模式: 可以结合命令模式一起使用, 解释器模式 先将 特性语言转为 python可运行的 代码(其实用户 输入的就是命令),然后 匹配对应的 命令后 交由 命令模式进行后续 执行命令 等等处理;(如 上述 智能家居 示例)

 

示例代码部分

如下 代码 在 <<精通Python设计模式>>书籍 示例代码基础上,稍微进行重构; 

# 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(user_command):
    """
    通过 pyparsing包 定义解析规则,然后 解析用户的命令,并执行命令
    :param user_command:
    :return:
    """
    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()

    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}

    t = user_command
    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__':
    # 用户输入的几条 字符串命令
    tests = ['open -> gate',
             'close -> garage',
             'turn on -> aircondition',
             'turn off -> heating',
             'increase -> boiler temperature -> 5 degrees',
             'decrease -> fridge temperature -> 2 degrees']
    # 依次 使用解释器 解析命令 并运行
    for command in tests:
        main(command)

 

总结: 

此模式 说白了 就是  将用户的 具有一定规则的 字符串命令 解析为 python可运行的命令;  解析字符串命令 可以通过 pyparsing包,或者正则 或者 自己编写匹配规则 进行解析;

此模式  实际应用的机会非常少; 但是如果遇到此类需求, 需要 知道 可以 使用 pyparsing,正则等进行 处理;

 

 

相关链接:

http://c.biancheng.net/view/1402.html

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值