今天我们介绍的设计模式叫做命令模式(command),在这个模式中,我们可以实现do和undo的解耦,让用户不需要关心内部实现细节。命令模式是我们日常生活中经常使用的一种模式。举个很简单的例子,比如我们发布代码。发布后,我们发现不小心发布了一个bug。这个时候怎么办?很简单,回滚就行了,把网上的代码回滚到本次发布之前的代码。这样一来,我们的release带来的变化就会被消除,bug就会避免。那么,对于一个发布系统来说,它需要做什么呢?其实有两个作用,一个是release,一个是rollback。这两个操作是相互可逆的。对于它的用户来说,他们并不关心它在内部是如何实现的。我们只需要按下页面上的按钮。让我们回顾一下这个过程。我们点击Publish,将最新的代码发布到网上。如果发布后发现问题,点击回滚,系统会自动恢复到发布前的状态。发布和回滚彼此可逆。当我们排除bug后,再次点击Publish,再次发布最新的代码。命令模式就是做这个的,也就是对do和undo的封装。让我们看一个非常简单的例子,重命名文件。例如,我们要将系统中的文件从A.txt重命名为B.txt。这个功能非常简单。系统为我们提供了一个现成的函数,叫做os.rename()。我们只需要将A和B这两个文件的地址传入即可。如果我们发现我们改错了名字,想要回滚怎么办?我们会发现改之前的名字忘记了,不知道怎么回滚。这时候就可以使用命令模式了。我们看一下代码:(self.dest,self.src)defrename(self,src,dest):print('renamingfrom{}to{}'.format(src,dest))os.rename(src,dest)在execute方法中,我们将文件从src更改为dest,如果我们要回滚,它会再次调用rename。将文件名从dest回滚到src。这样,作为用户,你完全不需要了解api的内部实现逻辑。否则,为了防止出错,需要做大量的适配。菜单项有了命令模式后,我们就可以在外面封装起来,进行ui交互。我们非常常见的UI交互方式之一就是按钮。点击某个按钮后,会出现按下的标志,实现什么功能。再按一次,标志消失,功能关闭。我刚刚找了一个例子,比如下图中的showminimap和showbreadcrumbsinthemenu,都是这样的功能。单击可显示缩略图,再次单击可使缩略图消失。如果你写过UI页面,一般来说,我们会先定义一个MenuItem类,它代表了菜单中所有item的基类。不同的选项代表不同的项目。进一步分析后,我们会发现有些项目需要双击关闭机制,而有些项目则不需要。比如上面的Run、Output等项,都是一次执行一次。当然,我们可以直接将上面介绍的Command对象作为一个item来使用,但是这样不利于整个菜单的统一,所以我们会在外面包裹一层。比如所有MenuItem的父类应该是这样的:classMenuItemBaseClass:def__init__(self):passdefpressed(self):passdefunpress(self):pass有了这个基类,我们就可以实现一个可以回滚的类。该对象作为类成员变量,然后在其中实现了unpress方法::self._command.undo()这样,我们的UI就和命令解耦了。如果我们要实现不同的可以回滚的功能,只需要实现不同的命令创建实例即可。对整个UI的使用没有影响,UI组件中使用的所有类都是统一的。在Python这样的弱类型语言中可能不明显,因为我们的列表说是菜单基类的列表,但实际上它可以加载任何东西。但是如果是强类型语言,那么这种抽象和封装就非常有必要了。
