这个模式的角色比较多:
Command:接口定义,只包含一个do方法,子类实现调用对应的Receiver的操作
Client:负责创建Command对象;传递Command对象给Invoker。
Invoker:执行Command对象的do操作。
Receiver:命令的实际执行者。
举个非常简单的例子:客户要求实现一个格式化工具,只包含一个按钮 Format和一个菜单项 Open用来打开文件。
另外客户给我们提供了格式化的实现类:
class XMLObject{ private String content; public XMLObject(String fileName){ content=文件内容; } public void formatXML(){ content=格式化后的文件内容 } public void saveXML(){ //保存content到文件 }}
根据需求我们可以按照下面的操作流程开发:
(1)点击 Open菜单:选择文件,创建XMLObject对象;
(2)点击 Format按钮:调用XMLObject对象的fotmatXML方法和saveXML方法。
接下来,代码怎么编写呢?
(1)实现Open菜单的功能:
Object currentObject = new XMLObject(filename);
(2)实现format按钮功能:
currentObject.format();currentObject.save();
嗯,代码看上去比较简单。
后续需求:客户又提供了JSONObject、SQLObject类,让我们实现。
于是,我们这样做:
(1)实现 Open菜单的功能:
if(文件后缀==.XML) Object currentObject = new XMLObject(filename);else if(文件后缀==.JSON) Object currentObject = new JSONObject(filename);else if(文件后缀==.SQL) Object currentObject = new SQLObject(filename);
(2)实现 format按钮功能:
if(currentObject instanceof XMLObject ){ currentObject.formatXML(); currentObject.saveXML(); return;}if(currentObject instanceof JSONObject ){ currentObject.formatJSON(); currentObject.saveJSON(); return;}if(currentObject instanceof SQLObject ){ currentObject.formatSQL(); currentObject.saveSQL(); return;}
可以看到,每增加一个类型的文件支持,就要format按钮的代码,随着类型的不断增加,format的判断条件会越来越多,并且二者耦合在一起。
如何去耦合?
(1)可以定义一个接口
interface FormatCommand{ do();}
然后让format按钮调用Command.do();这样再来了新的对象我就不用修改format操作了,而且再新增其他的按钮,比如unformat,undo等我也可以这样做。
(2)创建三个命令类:
class XMLFormatCommand implements FormatCommand{ XMLObject currentObject; public XMLFormatCommand(XMLObject obj){ currentObject = obj; } public do(){ currentObject.formatXML(); currentObject.saveXML(); }}class JSONFormatCommand implements FormatCommand{ JSONObject currentObject; public JSONFormatCommand(JSONObject obj){ currentObject = obj; } public do(){ currentObject.formatJSON(); currentObject.saveJSON(); }}class SQLFormatCommand implements FormatCommand{ SQLObject currentObject; public SQLFormatCommand(SQLObject obj){ currentObject = obj; } public do(){ currentObject.formatSQL(); currentObject.saveSQL(); }}
(3)实现 Open菜单的功能:
if(文件后缀==.XML) FormatCommand currentCommand = new XMLFormatCommand(XMLObject(filename));else if(文件后缀==.JSON) FormatCommand currentCommand = newJSONFormatCommand(JSONObject(filename));else if(文件后缀==.SQL) FormatCommand currentCommand =new JSONFormatCommand(JSONObject(filename));
(4)实现 format按钮功能:
currentCommand.do();
其实这就是命令模式:
Client:打开菜单
Invoker:format按钮
Receiver:三个Object
Command:FormatCommand三个实现
思考:
这样有什么好处呢?代码也没减少啊?
是的,代码量没有减少,不过这样符合了代码的开闭原则,去除了format按钮和用户提供的Object对象的耦合,有利于后续的扩展。