命令模式定义 命令模式(Command Pattern) 将一个 [ 请求 | 命令 | 调用方法 ] 封装为一个对象,从而可用不同的 [ 请求 | 命令 | 调用方法 ] 对客户进行参数化,对 [ 请求 | 命令 | 调用方法 ] 排队或记录其日志,以及支持可撤销的操作。
命令模式可以对发送者(Sender)和接收者(Receiver)完全解耦(Decoupling) 。发送者 是请求操作(发出命令)的对象,接收者 是接收请求(接收命令)并执行某相应操作的对象。
命令模式结构图
图中:
Command表示抽象命令类,它用于声明执行操作的一个接口;
ConcreteCommand表示具体命令类,它将一个接收者对象绑定于一个动作,实现在Command中声明的execute()方法,调用接收者的相关操作(Action);
Client表示客户应用程序,创建一个具体命令类的对象,并且设定它的接收者;
Invoker表示调用者,要求一个命令对象执行一个请求;
Receiver表示接收者,它实现如何执行关联请求的相关操作。
命令模式实例——公告板系统 实例说明 某软件公司欲开发一个基于Windows平台的公告板系统。系统提供一个主菜单(Menu),在主菜单中包含了一些菜单项(MenuItem),可以通过Menu类的addMenuItem()方法增加菜单项。菜单项的主要方法是click(),每一个菜单项包含一个抽象命令类,具体命令类包括OpenCommand(打开命令)、CreateCommand(新建命令)、EditCommand(编辑命令)等,命令类具有一个execute()方法,用于调用公告板系统界面类(BoardScreen)的open()、create()、edit()等方法。现使用命令模式设计该系统,使得MenuItem类与BoardScreen类的耦合度降低,绘制类图并编程实现。
实例类图
实例类图中与命令模式结构图对应的类主要有:
实例代码 Command.java:
interface Command { public void execute () ; }
MenuItem.java:
class MenuItem { private String name; private Command command; public MenuItem (String name) { this .name = name; } public String getName () { return this .name; } public void setName (String name) { this .name = name; } public Command getCommand () { return this .command; } public void setCommand (Command command) { this .command = command; } public void click () { command.execute(); } }
Menu.java:
import java.util.*;class Menu { public ArrayList itemList = new ArrayList(); public void addMenuItem(MenuItem item) { itemList.add(item); } }
OpenCommand.java:
class OpenCommand implements Command { private BoardScreen screen; public OpenCommand (BoardScreen screen) { this .screen = screen; } public void execute () { screen.open(); } }
CreateCommand.java:
class CreateCommand implements Command { private BoardScreen screen; public CreateCommand (BoardScreen screen) { this .screen = screen; } public void execute () { screen.create(); } }
EditCommand.java:
class EditCommand implements Command { private BoardScreen screen; public EditCommand (BoardScreen screen) { this .screen = screen; } public void execute () { screen.edit(); } }
BoardScreen.java:
class BoardScreen { private Menu menu; private MenuItem openItem,createItem,editItem; public BoardScreen () { menu = new Menu(); openItem = new MenuItem("打开" ); createItem = new MenuItem("新建" ); editItem = new MenuItem("编辑" ); menu.addMenuItem(openItem); menu.addMenuItem(createItem); menu.addMenuItem(editItem); } public void display () { System.out .println("主菜单选项:" ); for (Object obj:menu.itemList) { System.out .println(((MenuItem)obj).getName()); } } public void open () { System.out .println("显示打开窗口" ); } public void create () { System.out .println("显示新建窗口" ); } public void edit () { System.out .println("显示编辑窗口" ); } public Menu getMenu () { return menu; } }
Client.java:
//客户端测试类 class Client { public static void main(String args []) { BoardScreen screen = new BoardScreen () ; Menu menu = screen.getMenu() ; Command openCommand,createCommand,editCommand; openCommand = new OpenCommand (screen ) ; createCommand = new CreateCommand (screen ) ; editCommand = new EditCommand (screen ) ; MenuItem openItem,createItem,editItem; openItem = (MenuItem ) menu.itemList.get(0) ; createItem = (MenuItem ) menu.itemList.get(1) ; editItem = (MenuItem ) menu.itemList.get(2) ; openItem.setCommand(openCommand ) ; createItem.setCommand(createCommand ) ; editItem.setCommand(editCommand ) ; screen.display() ; openItem.click() ; createItem.click() ; editItem.click() ; } }
运行结果
附加描述 在本实例中,只需要在调用者MenuItem中注入不同的具体命令类,可以使得相同的菜单项MenuItem对应接收者BoardScreen的不同方法。无需修改类库代码,只需修改客户端代码即可更换接收者。
在实际开发时,还可以将BoardScreen中的open()、create()和edit()等方法封装在不同的类中,如果需要更换某菜单项的功能,只需对应增加一个新的具体命令类和一个接收者类,再将新的具体命令对象注入对应的MenuItem对象,即可实现菜单项功能的改变,且符合开闭原则。
注:开闭原则(Open-Closed Principle, OCP)由Bertrand Meyer提出,其定义为“Software entities should be open for extension, but closed for modification.”也就是说软件实体应对扩展开放,而对修改关闭,即软件实体应尽量在不修改原有代码的情况下进行扩展。
后话 今天花了好长时间,才对命令模式有了基本的理解。感慨设计模式学路之漫漫,愚将日积跬步而求索。