意图
将一个请求封装为对象,以便构建、传递和调用。这里的请求可以是动作或事务。
动机
有时知道需要对某些接收者提交请求,但并不知道具体要向哪一个接受者提出该请求。下面以很多应用程序中出现的菜单为例进行说明。在设计菜单的时候,虽然清楚地知道需要做什么,但是作为这些菜单的操作对象的数据有可能还没有生成。
解决方法首先定义封装菜单动作的抽象类Command,它定义执行该动作的Execute操作。在具象Command类的Execute操作中,接收者会执行该请求。一个常用的做法是,把接受者作为具象Command类的实例变量(C++中的数据成员)来管理。
上图是在应用程序菜单功能中使用Command时的示例。Application管理一个Menu实例,而Menu实例又管理若干MenuItem实例。每个MenuItem代表一个菜单项。于此同时,Application还会管理一个Document实例,用以表达打开的文档。
为实现菜单项的动态配置,软件让每个MenuItem持有一个Command类型的实例变量。当用户选择某个菜单项时,对应的MenuItem就会调用对应的Command对象的Execute方法。因为Command成员的类型为基类类型,因此MenuItem并不知晓Command成员到底属于那个子类。
假设存在一个PasteCommand,其功能是向文档粘贴正文。这个命令的接收者是一个文档,其内容是由Application在更新Document实例时通知给PasteCommand。PasteCommand在自己的Execute方法中,使用Document实例进行各种处理从而实现粘贴动作。
另外一个OpenCommand的功能是为Application打开一个新文档。它的接收者是Application本身,是整个应用中第一个创建的对象。当Application创建该MenuItem时,可以以自己的引用直接作为参数传递给OpenCommand的构造函数。当用户选择OpenDocument菜单项的时候,OpenCommand在自己的Execute方法中调用Application的方法以打开新文档。
PasteCommand和OpenDocument这两个Command的具象类具有相同的Execute接口,但在其内部面对不同接收者执行完全不同的操作。
注:文中类图来自《设计模式》一书。
觉得本文有帮助?请分享给更多人。
阅读更多更新文章,请扫描下面二维码,关注微信公众号【面向对象思考】