行为的封装——模式系列谈之Command模式

    技术2022-05-11  153

                                         行为的封装——模式系列谈之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模式恰恰很好地做到了这一点。 首先,我们来给我们的行为定义一个接口: 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到相应的常量里,而对客户端则无须做任何修改。

    最新回复(0)