解释器模式
我们前边介绍的大部分设计模式都是可以广泛应用于各种领域的,但是实际上也有许多设计模式是局限于一些专门领域的,比如解释器模式。
解释器模式(Interpreter Pattern)的用途相当专一:构建语言解释器。
关于编译原理的内容我早还给老师了,也不可能为了学习这个模式专门回头去看,所以这里简单地描述一下《Head First 设计模式》中介绍地内容就OK了,如果有兴趣地童鞋可以自行查找相关资料深入学习。
解释器模式就是帮助构建一个解释器的设计模式:
这个设计的关键在于我们将各种语法和词法分析功能拆解为各个组件,比如图中的Variable
就负责分析给定的语句后从中创建相关变量,Repetition
负责分析出其中的循环语法执行循环。
当然和这种设计模式相比,显然学习编译原理本身要困难的多的多,所以,大家看看就好...
中介者模式
中介者模式(Mediator Pattern)用于集中相关对象之间复杂的沟通和控制方式。
假设我们现在有一套智能家居,需要通过各种智能家居设备的交互满足不同场景的需求,比如说在打开电视看电影的时候,需要将灯光调暗,冰箱制冰,如果室外高温还要打开空调。这就需要不同的智能家居设备之间进行交互。
整个系统的UML可能是这样的:
如果你的使用场景更为复杂,则可能上面的图会更复杂。
这样的系统的缺陷显而易见,设备之间的耦合度过高,任意一个设备的修改可能会影响到与之关联的若干个设备。
在这种情况下我们可以使用中介者模式进行改进:
在上面的设计中,网关Gateway
扮演了中介者的角色,充当所有设备的沟通“中介”,通过使用中介者模式,我们将原有系统中的设备之间进行了解耦,这简化了可能需要的后续对系统的扩展和维护工作。
中介者模式的缺陷在于因为所有的其它角色都要依靠中介者来进行沟通工作,所以中介者本身承担了大量任务,如果设计不当,很可能让中介者本身变得过于复杂。
备忘录模式
备忘录模式(MementoPatter)旨在让你的系统具备可以从之前的某个状态恢复的能力。
假设我们需要开发一个电脑游戏,这是一个闯关游戏,每次闯关失败后需要回到这一关的初始状态,这可能不仅需要恢复管卡进度,可能还需要还原角色身上的一些装备等。
为了可以完成这一功能,我们可以采取这种做法:
-
在需要保存游戏的时候产生一个当前游戏的“快照”。
-
在需要还原游戏的时候加载一个已有的游戏“快照”。
事实上这也是计算机领域各种软件都采取的备份-还原机制。
这个设计本身很简单,只要提供两个方法,一个用于提供当前要保存的内容的快照,另一个可以使用已有快照进行加载。
这个设计的难点在于产生快照以及加载快照的逻辑,以及快照的持久化保存。这一点在Java中可以利用对象的序列化功能。
原型模式
如果你学习过Python中的元类相关内容,一定对原型这一概念不陌生。
事实上类是创建对象的蓝图,可以看作是对象的原型,而Python中的元类就是类的原型。
所以我认为原型模式的主旨在于利用已有的原型生成对象。至于你的原型是类、对象还是元类,这并不重要,这些只是具体的手段。
因此我认为使用类创建对象这一行为本身就是在使用原型模式,只不过这种用方式太过常见,不值得讨论罢了。
如果你的系统中需要创建对象,但直接通过定义类来创建不是很合适的时候,你就可以思考通过其他手段来实现原型模式。
这里我认为主要有两种方式:
-
用已有对象作为原型,进行拷贝。
-
使用元类。
前者很好理解,也是最常用的方式,在Java中调用obj.clone()
即可生成对象的副本,如果某个对象创建过程非常消耗资源,我们完全可以在需要一个新的该对象时不从头创建,直接从一个已有对象拷贝一个副本即可。
后者就相对复杂一些,元类是类的原型,我们可以通过定义元类来修改类的行为,进而影响到对象的创建。
关于元类的更多内容可以阅读。
访问者模式
访问者模式(Visitor)旨在通过添加一个可以对组合结构进行遍历的访问者,实现对组合结构的功能扩展。
假设我们现在有一个点餐系统,每个菜单项有一个属性说明其是否为素菜,现在有一些激进的素食主义者,认为只有素食是健康的,所以要求我们给菜单项和菜单都标注是否健康食(他们并不会仔细去看是否是素菜,只会看是否有健康标识)。为此我们必须给菜单项和菜单添加一个isHealthyFoods
的方法,如果类似的要求增多,我们就可能需要添加越来越多的方法,有没有其它选择?
这里的示例来自于。
这就是访问者模式存在的意义。
因为我们的菜单和菜单项由树形结构构建,并且实现了迭代器,所以我们的访问者可以简单的创建为:
from .node import Node
from .menu_item import MenuItem
class Visitor:
def __init__(self, node:Node) -> None:
self._root = node
def isHealthyFoods(self)->bool:
node:Node
for node in self._root:
if isinstance(node, MenuItem):
if not node.vegetarain:
return False
return True
测试代码如下:
import os
import sys
parentDir = os.path.dirname(__file__)+"\\.."
sys.path.append(parentDir)
from visitor.src.menu_factory import MenuFactory
from visitor.src.visitor import Visitor
menu = MenuFactory.getBarbecueHouseMenu()
for node in menu:
healthyFlag: str = ""
if (Visitor(node).isHealthyFoods()):
healthyFlag = "健康食物"
else:
healthyFlag = "不健康食物"
print("{!s} [{}]".format(node, healthyFlag))
通过使用访问者Visitor(node).isHealthyFoods()
,我们给树形结构添加了原本没有的功能isHealthyFoods
,并且没有对已有的树形结构的相关组件进行任何修改。
访问者模式的缺点是我们的访问者必然依赖于树形结构中的组件,相关组件如果发生改变,我们的访问者可能也会受到影响,这增加了树形结构的复杂度。
完整的示例代码见。
好了,到这里《Head First 设计模式》的内容就全部介绍完毕了,希望大家有所收获,谢谢阅读。
我会考虑开一个新的系列,可能是Linux,也可能是算法。
文章评论