行为的封装——模式系列谈之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到相应的常量里,而对客户端则无须做任何修改。
转载请注明原文地址: https://ibbs.8miu.com/read-2848.html