行为的封装——模式系列谈之Command模式
解决一个问题,我们总是先将这个问题进行分解,分解成为很多小块,或者方面;然后我们关注问题的每一个方面,将他们一一解决;最后完成了对整个问题的解决。在我们的模式的解决思路中,都贯穿了这种思想。模式总是不厌烦的将各种依赖进行解耦,然后去关注问题的各个方面。
一般说来,Command模式可以从以下两个方面进行理解:
第一,Command模式成功的对行为和行为的调用者之间进行了解耦,解除了行为和其调用者之间的依赖,让调用者依赖行为的接口,符合了依赖颠倒原则。这无疑使得代码的扩展性有了很大的提高。
第二,Command模式对对象的行为进行了分解,让我们对对象的各个行为一一关注,然后完成对对象的关注。
现在我们来看一个Command模式的简单例子:
package command;
public interface Command {
public void doCommand();
}
这是行为的接口,行为的调用者只需要直接和它打交道。
行为的实现:
package command;
public class OnCommand implements Command {
public void doCommand()
{
System.out.println("The light is turning on……");
}
}
package command;
public class OffCommand implements Command {
public void doCommand()
{
System.out.println("The light is turning off……");
}
}
Command模式将行为封装在类里,适用于在对对象的行为特别关注的场合。
测试代码:
Command[] commands = Command[]{new OnCommand(),new OffCommand()};
Int I=1;
Commands[i].doCommand();
测试结果:
The light is turning off.…...
上面只是一个最简单的Command模式的例子,只是用来我们对该模式作初步的理解。实际的Command模式的应该可没有这么简单。
谈到Command模式,有些人一方面同意Command模式解耦了行为和行为的调用者之间的依赖;另一方面却抱怨Command模式使得简单问题复杂化,在他们看来,将这么多的行为独立出来,十分麻烦,换来的系统的扩展性未必用得上。果然事情是他们所想得那样吗?我们来看下面的例子:
public class DictionaryDetailAction extends BaseAction
{
public ActionForward actionExecute(ActionMapping actionMapping, ActionForm actionForm,
HttpServletRequest request, HttpServletResponse response)
{
DynaActionForm dictForm = (DynaActionForm)actionForm;
String dictId = request.getParameter("dict_id");
String action = request.getParameter("req_action");
DictionaryManager dictManager = new DictionaryManager(getAdminFacade(),getTransactionManager());
Object dictObj = dictForm.get("dictId");
DictionaryData data = new DictionaryData();
data.setDictID( StringUtils.strObjectToLong(dictObj) );
String dictName = StringUtils.strObjectToString(dictForm.get("dictName"));
String dictDesc = StringUtils.strObjectToString(dictForm.get("dictDesc"));
data.setDictName(dictName);
data.setDictDesc(dictDesc);
logger.debug("DictionaryDetailAction................" + dictId);
if(dictId == null || "".equals(dictId))
{
dictId = StringUtils.longToString(data.getDictID());
}
DictionaryData dict = dictManager.getDictionaryData(StringUtils.stringToLong(dictId));
dictForm.set("dictId", StringUtils.longToString(dict.getDictID()));
dictForm.set("dictName", dict.getDictName());
dictForm.set("dictDesc", dict.getDictDesc());
if(OperationConstants.OPERATION_UPDATE.equalsIgnoreCase(action))
{
try
{
dictManager.updateDictionaryData(data);
logger.debug("updateDictionaryData");
}
catch(Exception e)
{
logger.error("actionExecute", e, "updateDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
}
else if(OperationConstants.OPERATION_ADD.equalsIgnoreCase(action))
{
logger.debug("OPERATION_ADD");
dictForm.set("dictName", "");
dictForm.set("dictDesc", "");
}
else if(OperationConstants.OPERATION_SAVE.equalsIgnoreCase(action))
{
DictionaryData data2 = dictManager.getDictionaryData(StringUtils.stringToLong(dictId));
DictionaryData data3 = new DictionaryData();
data3.setDictID((new Sequence()).getSequence(TableNameConstants.HR_DICTIONARY));
data3.setDictName(dictName);
data3.setDictDesc(dictDesc);
data3.setParentDictID(ObjectUtils.stringToInt(dictId));
data3.setModuleID(data2.getModuleID());
data3.setCategoryID(data2.getCategoryID());
try
{
dictManager.insertDictionaryData(data3);
logger.debug("insertDictionaryData");
}
catch(Exception e)
{
logger.error("actionExecute", e, "insertDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
}
request.setAttribute("attr_action", action);
return actionMapping.findForward("success");
}
}
解决一个问题,我们总是先将这个问题进行分解,分解成为很多小块,或者方面;然后我们关注问题的每一个方面,将他们一一解决;最后完成了对整个问题的解决。在我们的模式的解决思路中,都贯穿了这种思想。模式总是不厌烦的将各种依赖进行解耦,然后去关注问题的各个方面。
一般说来,Command模式可以从以下两个方面进行理解:
第一,Command模式成功的对行为和行为的调用者之间进行了解耦,解除了行为和其调用者之间的依赖,让调用者依赖行为的接口,符合了依赖颠倒原则。这无疑使得代码的扩展性有了很大的提高。
第二,Command模式对对象的行为进行了分解,让我们对对象的各个行为一一关注,然后完成对对象的关注。
现在我们来看一个Command模式的简单例子:
package command;
public interface Command {
public void doCommand();
}
这是行为的接口,行为的调用者只需要直接和它打交道。
行为的实现:
package command;
public class OnCommand implements Command {
public void doCommand()
{
System.out.println("The light is turning on……");
}
}
package command;
public class OffCommand implements Command {
public void doCommand()
{
System.out.println("The light is turning off……");
}
}
Command模式将行为封装在类里,适用于在对对象的行为特别关注的场合。
测试代码:
Command[] commands = Command[]{new OnCommand(),new OffCommand()};
Int I=1;
Commands[i].doCommand();
测试结果:
The light is turning off.…...
上面只是一个最简单的Command模式的例子,只是用来我们对该模式作初步的理解。实际的Command模式的应该可没有这么简单。
谈到Command模式,有些人一方面同意Command模式解耦了行为和行为的调用者之间的依赖;另一方面却抱怨Command模式使得简单问题复杂化,在他们看来,将这么多的行为独立出来,十分麻烦,换来的系统的扩展性未必用得上。果然事情是他们所想得那样吗?我们来看下面的例子:
public class DictionaryDetailAction extends BaseAction
{
public ActionForward actionExecute(ActionMapping actionMapping, ActionForm actionForm,
HttpServletRequest request, HttpServletResponse response)
{
DynaActionForm dictForm = (DynaActionForm)actionForm;
String dictId = request.getParameter("dict_id");
String action = request.getParameter("req_action");
DictionaryManager dictManager = new DictionaryManager(getAdminFacade(),getTransactionManager());
Object dictObj = dictForm.get("dictId");
DictionaryData data = new DictionaryData();
data.setDictID( StringUtils.strObjectToLong(dictObj) );
String dictName = StringUtils.strObjectToString(dictForm.get("dictName"));
String dictDesc = StringUtils.strObjectToString(dictForm.get("dictDesc"));
data.setDictName(dictName);
data.setDictDesc(dictDesc);
logger.debug("DictionaryDetailAction................" + dictId);
if(dictId == null || "".equals(dictId))
{
dictId = StringUtils.longToString(data.getDictID());
}
DictionaryData dict = dictManager.getDictionaryData(StringUtils.stringToLong(dictId));
dictForm.set("dictId", StringUtils.longToString(dict.getDictID()));
dictForm.set("dictName", dict.getDictName());
dictForm.set("dictDesc", dict.getDictDesc());
if(OperationConstants.OPERATION_UPDATE.equalsIgnoreCase(action))
{
try
{
dictManager.updateDictionaryData(data);
logger.debug("updateDictionaryData");
}
catch(Exception e)
{
logger.error("actionExecute", e, "updateDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
}
else if(OperationConstants.OPERATION_ADD.equalsIgnoreCase(action))
{
logger.debug("OPERATION_ADD");
dictForm.set("dictName", "");
dictForm.set("dictDesc", "");
}
else if(OperationConstants.OPERATION_SAVE.equalsIgnoreCase(action))
{
DictionaryData data2 = dictManager.getDictionaryData(StringUtils.stringToLong(dictId));
DictionaryData data3 = new DictionaryData();
data3.setDictID((new Sequence()).getSequence(TableNameConstants.HR_DICTIONARY));
data3.setDictName(dictName);
data3.setDictDesc(dictDesc);
data3.setParentDictID(ObjectUtils.stringToInt(dictId));
data3.setModuleID(data2.getModuleID());
data3.setCategoryID(data2.getCategoryID());
try
{
dictManager.insertDictionaryData(data3);
logger.debug("insertDictionaryData");
}
catch(Exception e)
{
logger.error("actionExecute", e, "insertDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
}
request.setAttribute("attr_action", action);
return actionMapping.findForward("success");
}
}
从上面的例子我们可以看到,笼统地将行为的实现放在客户端,会造成多方面的困惑:第一,编码的困难,外部变量对行为体的影响,可能在行为体内部使用了与外部相同的变量而不觉查;可能造成各个行为之间的相互混淆;等等一系列的问题。第二,调试的困难,在调试过程中,可能造成程序调试的混乱,你可能不知道程序跑到了那个行为体去了,等等。
总之,将所有的行为混在了一起,容易造成思维的混乱。解决的办法还是需要我们将问题分解开来,我们需要一一关注每一个行为。而我们的Command模式恰恰很好地做到了这一点。
首先,我们来给我们的行为定义一个接口:
public interface WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping);
}
然后,我们来实现各个行为:
public class Update implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
try
{
dictManager.updateDictionaryData(data);
//logger.debug("updateDictionaryData");
return actionMapping.findForward("success");
}
catch(Exception e)
{
//logger.error("actionExecute", e, "updateDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
}
}
public class Add implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
dictForm.set("dictName", "");
dictForm.set("dictDesc", "");
return actionMapping.findForward("success");
}
}
public class Save implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
DictionaryData data2 = dictManager.getDictionaryData(StringUtils.stringToLong(dictId));
DictionaryData data3 = new DictionaryData();
data3.setDictID((new Sequence()).getSequence(TableNameConstants.HR_DICTIONARY));
data3.setDictName(dictName);
data3.setDictDesc(dictDesc);
data3.setParentDictID(ObjectUtils.stringToInt(dictId));
data3.setModuleID(data2.getModuleID());
data3.setCategoryID(data2.getCategoryID());
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
try
{
dictManager.insertDictionaryData(data3);
//logger.debug("insertDictionaryData");
return actionMapping.findForward("success");
}
catch(Exception e)
{
logger.error("actionExecute", e, "insertDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
}
}
public class NothingDo implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
return actionMapping.findForward("success");
}
}
这样我们可以看到,将各个行为独立出来以后,我们对各个行为进行了单独的关注,使得编码和测试都变得更为简单。这是Command模式带给我们的第一个好处。
然后我们看看客户端的调用情况:
WebAction command;
if(OperationConstants.OPERATION_UPDATE.equalsIgnoreCase(action))
{
command = new Update();
}
else if(OperationConstants.OPERATION_ADD.equalsIgnoreCase(action))
{
command = new Add();
}
else if(OperationConstants.OPERATION_SAVE.equalsIgnoreCase(action))
{
command = new Save();
}
else
{
command = new NothingDo();
}
command.doAction();
现在我们的客户端变得极为简单和清晰,我们的Command模式的确让我们的编码和测试简单起来;同时,她也给我们的系统的扩展带来了一定的可扩展性。如果我们要增加新的行为,就不需要在客户端大动干戈了,只需要增加一个行为类,然后到客户端作相应的改动。
之所以说Command模式给我们的系统带来了一定的可扩展性,是因为客户端和行为的现实还存在一定的依赖,所以我们增加了一种行为,就不得不去对客户端进行相应的改动,虽然这种改动比不用Command模式小得多。
到了这里,我们就一定会想,能不能将这种依赖关系完全去掉呢?我们说,当然可以。
解决的办法是让Command模式结合Factory模式,这样使得客户端完全不依赖具体的行为。
首先,我们来做一个工厂,让它来生产行为:
public class Factory
{
public static WebAction getAtion(String name)
{
try
{
Class cls = Class.forName(name);
Return (WebAction)cls. newInstance();
}
catch(Exception e)
{
return null;
}
}
}
然后我们在一个常量类OperationConstants里存储下列常量:
final String PATH = “”;
final String[] ACTIONNAMES = new String[]{“UPDATE”,”ADD”,”SAVE”};
fianl String[] CLASSNAMES = new String[]{PATH+”Update”,PATH+”Add”,PATH+”Save”};
最后客户端的代码为:
for(int I=0;I< OperationConstants.ACTIONNAMES.length;I++)
{
if(OperationConstants.ACTIONNAMES[i]. equalsIgnoreCase(action))
{
Factory.getAction(OperationConstants.CLASSNAMES[i]).doAction();
Break();
}
}
这样就完全将具体的行为类和客户端解耦开来,如果我们需要增加一个行为delete的话,那么我们只需要增加一个Delete类来实现WebAction接口,然后在常量类里增加actionName和className到相应的常量里,而对客户端则无须做任何修改。
总之,将所有的行为混在了一起,容易造成思维的混乱。解决的办法还是需要我们将问题分解开来,我们需要一一关注每一个行为。而我们的Command模式恰恰很好地做到了这一点。
首先,我们来给我们的行为定义一个接口:
public interface WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping);
}
然后,我们来实现各个行为:
public class Update implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
try
{
dictManager.updateDictionaryData(data);
//logger.debug("updateDictionaryData");
return actionMapping.findForward("success");
}
catch(Exception e)
{
//logger.error("actionExecute", e, "updateDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
}
}
public class Add implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
dictForm.set("dictName", "");
dictForm.set("dictDesc", "");
return actionMapping.findForward("success");
}
}
public class Save implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
DictionaryData data2 = dictManager.getDictionaryData(StringUtils.stringToLong(dictId));
DictionaryData data3 = new DictionaryData();
data3.setDictID((new Sequence()).getSequence(TableNameConstants.HR_DICTIONARY));
data3.setDictName(dictName);
data3.setDictDesc(dictDesc);
data3.setParentDictID(ObjectUtils.stringToInt(dictId));
data3.setModuleID(data2.getModuleID());
data3.setCategoryID(data2.getCategoryID());
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
try
{
dictManager.insertDictionaryData(data3);
//logger.debug("insertDictionaryData");
return actionMapping.findForward("success");
}
catch(Exception e)
{
logger.error("actionExecute", e, "insertDictionaryData occur error");
dictForm.set("dictName", ConversionResource.getGBStr(dictName));
dictForm.set("dictDesc", ConversionResource.getGBStr(dictDesc));
return actionMapping.getInputForward();
}
}
}
public class NothingDo implements WebAction
{
public ActionForward doAction(DictionaryData data, DictionaryManager dictManager, DynaActionForm dictForm,ActionMapping actionMapping)
{
return actionMapping.findForward("success");
}
}
这样我们可以看到,将各个行为独立出来以后,我们对各个行为进行了单独的关注,使得编码和测试都变得更为简单。这是Command模式带给我们的第一个好处。
然后我们看看客户端的调用情况:
WebAction command;
if(OperationConstants.OPERATION_UPDATE.equalsIgnoreCase(action))
{
command = new Update();
}
else if(OperationConstants.OPERATION_ADD.equalsIgnoreCase(action))
{
command = new Add();
}
else if(OperationConstants.OPERATION_SAVE.equalsIgnoreCase(action))
{
command = new Save();
}
else
{
command = new NothingDo();
}
command.doAction();
现在我们的客户端变得极为简单和清晰,我们的Command模式的确让我们的编码和测试简单起来;同时,她也给我们的系统的扩展带来了一定的可扩展性。如果我们要增加新的行为,就不需要在客户端大动干戈了,只需要增加一个行为类,然后到客户端作相应的改动。
之所以说Command模式给我们的系统带来了一定的可扩展性,是因为客户端和行为的现实还存在一定的依赖,所以我们增加了一种行为,就不得不去对客户端进行相应的改动,虽然这种改动比不用Command模式小得多。
到了这里,我们就一定会想,能不能将这种依赖关系完全去掉呢?我们说,当然可以。
解决的办法是让Command模式结合Factory模式,这样使得客户端完全不依赖具体的行为。
首先,我们来做一个工厂,让它来生产行为:
public class Factory
{
public static WebAction getAtion(String name)
{
try
{
Class cls = Class.forName(name);
Return (WebAction)cls. newInstance();
}
catch(Exception e)
{
return null;
}
}
}
然后我们在一个常量类OperationConstants里存储下列常量:
final String PATH = “”;
final String[] ACTIONNAMES = new String[]{“UPDATE”,”ADD”,”SAVE”};
fianl String[] CLASSNAMES = new String[]{PATH+”Update”,PATH+”Add”,PATH+”Save”};
最后客户端的代码为:
for(int I=0;I< OperationConstants.ACTIONNAMES.length;I++)
{
if(OperationConstants.ACTIONNAMES[i]. equalsIgnoreCase(action))
{
Factory.getAction(OperationConstants.CLASSNAMES[i]).doAction();
Break();
}
}
这样就完全将具体的行为类和客户端解耦开来,如果我们需要增加一个行为delete的话,那么我们只需要增加一个Delete类来实现WebAction接口,然后在常量类里增加actionName和className到相应的常量里,而对客户端则无须做任何修改。