模式定义:
将用户 输入的 具有一定规则的 语句 通过 解释器转译为 能够用代码执行; 就是将 一种语言 解释成另一种语言;
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