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

                                     行为的封装——模式系列谈之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到相应的常量里,而对客户端则无须做任何修改。
阅读更多
个人分类: 模式
上一篇组合的魔力——模式系列谈之Decorator模式
下一篇简单就是美——由模式谈面向对象的基本原则之单一职责原则
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭