当前位置: 首页 > 科技观察

设计模式系列—命令模式

时间:2023-03-19 01:50:35 科技观察

模式定义将一个请求封装为一个对象,将发出请求的责任和执行请求的责任分离。这样,两者通过命令对象进行通信,方便了命令对象的存储、调用、添加和管理。在软件开发系统中,“方法请求者”和“方法实施者”之间往往存在紧耦合关系。这不利于软件功能的扩展和维护。比如处理“撤销、重做、记录”等行为不方便,那么“如何将方法的请求者与方法的实现者解耦”?变得很重要,命令模式可以很好的解决问题。这个问题。模板实现如下:Invokerir=newInvoker(cmd);System.out.println("客户端访问调用者的call()方法...");ir.call();}}//抽象命令interfaceCommand{publicabstractvoidexecute();}//具体命令classConcreteCommandimplementsCommand{privateReceiverreceiver;ConcreteCommand(){receiver=newReceiver();}publicvoidexecute(){receiver.action();}}//接收者classReceiver{publicvoidaction(){System.out.println("接收者的action()方法调用...");}}//调用者classInvoker{privateCommandcommand;publicInvoker(Commandcommand){this.command=command;}publicvoidsetCommand(Commandcommand){this.command=command;}publicvoidcall(){System.out.println("调用者执行命令command...");command.execute();}}输出结果如下:客户端访问调用者的call()方法...调用者执行命令。..调用接收者的action()方法...待解决的问题在一个软件系统中,行为请求者和行为实施者通常是紧耦合关系,但是在某些场合,比如需要记录,undoorredo,transaction这样无法抗拒变化的紧耦合设计是不适合处理的。模式组合可以将系统中的相关操作抽象为命令,使调用者与实现者分离。它的结构如下。实例说明结合命令方式,实例概览实现课程视频的打开和关闭。使用步骤第一步:声明执行命令的接口,用抽象方法execute()interfaceCommand{voidexecute();}第二步:定义具体的命令角色,创建打开课程链接和关闭课程链接/***打开课程链接*/classOpenCourseVideoCommandimplementsCommand{privateCourseVideocourseVideo;publicOpenCourseVideoCommand(CourseVideocourseVideo){this.courseVideo=courseVideo;}@Overridepublicvoidexecute(){courseVideo.open();}}/***关闭课程链接*/classCloseCourseVideoCommandimplementsCommand{privateCourseVideocourseVideo;publicCloseVideoCourse){CourseVideoComthis).courseVideo=courseVideo;}@Overridepublicvoidexecute(){courseVideo.close();}}第三步:定义接收者角色,执行命令函数的相关操作,成为具体命令对象业务类CourseVideo的真正实现者{privateStringname;publicCourseVideo(Stringname){this.name=name;}publicvoidopen(){System.out.println(this.name+"课程视频已开启。");}publicvoidclose(){System.out.println(this.name+"课程视频关闭。");}}第四步:创建一个User对象作为请求的发送者,即请求者角色);}publicvoidexecuteCommands(){commands.forEach(Command::execute);commands.clear();}}第五步:测试执行publicclassCommandPattern{publicstaticvoidmain(String[]args){//命令接收方patternseries");//创建命令OpenCourseVideoCommandopenCourseVideoCommand=newOpenCourseVideoCommand(courseVideo);CloseCourseVideoCommandcloseCourseVideoCommand=newCloseCourseVideoCommand(courseVideo);//创建执行器Useruser=newUser();//添加命令closeExecuteuser.executeCommands();}}Outputresult设计模式系列课程视频打开设计模式系列课程视频关闭优点是降低了系统的耦合度命令模式可以解耦对象从实现操作的对象调用操作。添加或删除命令非常方便。使用命令方式增删命令不会影响其他类,满足“开闭原则”,扩展更灵活。可以实现宏命令。命令模式可以与组合模式结合,将多个命令组合成一个组合命令,即宏命令。方便实现Undo和Redo操作。命令模式可以结合后面介??绍的备忘录模式,实现命令的撤销和恢复。缺点可能会生成大量特定的命令类。因为每一个具体的操作都需要设计一个具体的命令类,这样会增加系统的复杂度。应用场景命令执行过程复杂且可能发生变化,需要额外的操作来执行命令动作本身。这时候可以考虑使用命令模式。命令模式的扩展。在软件开发中,有时命令模式和组合模式一起使用,即构成宏命令模式,也叫组合命令模式。一个宏命令包含一组命令,它扮演着特定命令和调用者的双重角色。当它被执行时,它包含的所有命令都会被递归调用。具体结构图如下:模板实现如下:packagecom.niuh.designpattern.command.v2;importjava.util.ArrayList;/***

*Compositecommandpattern*

*/publicclassCompositeCommandPattern{publicstaticvoidmain(String[]args){AbstractCommandcmd1=newConcreteCommand1();AbstractCommandcmd2=newConcreteCommand2();CompositeInvokerir=newCompositeInvoker();ir.add(cmd1);ir.add(cmd2);System.out.println("客户端访问调用者的execute()方法...");ir.execute();}}//抽象命令interfaceAbstractCommand{publicabstractvoidexecute();}//叶子组件:具体命??令1classConcreteCommand1implementsAbstractCommand{privateCompositeReceiverreceiver;ConcreteCommand1(){receiver=newCompositeReceiver();}publicvoidexecute(){receiver.action1();}}//叶组件:具体命??令2/树枝c组件:调用者classCompositeInvokerimplementsAbstractCommand{privateArrayList<AbstractCommand>children=newArrayList();publicvoidadd(AbstractCommandc){children.add(c);}publicvoidremove(AbstractCommandc){children.remove(c);}publicAbstractCommandgetChild(inti){returnchildren.get(i);}publicvoidexecute(){for(Objectobj:children){((AbstractCommand)obj).execute();}}}//接收者类CompositeReceiver{publicvoidaction1(){System.out.println("接收者的action1()方法调用了...");}publicvoidaction2(){System.out.println("Receiver'saction2()methodiscalled...");}}输出结果如下:客户端访问调用者的execute()方法...接收方的action1()方法被调用...接收者的action2()方法被调用...命令模式也可以结合Memento模式使用,成为一个不可撤销的命令应用程序java.util中的scheduleXXX()方法模式源代码中的定时器类javaConcurrencyExecutorexecute()方法java.lang.reflect.Methodinvoke()方法org.springframework.jdbc.core.JdbcTemplate...在JdbcTem中的应用plateJdbcTemplate中命令方式的使用没有遵循标准命令方式的使用,但是思路是一样的Spring的JdbcTemplate类中有一个query()方法。在query()方法中定义了一个内部类QueryStatementCallback。QueryStatementCallback实现StatementCallback接口。还有其他实现此接口的类。StatementCallback接口中还有一个抽象方法。执行声明()。query()在execute()中再次被调用。StatementCallback充当命令角色,而JdbcTemplate既充当调用者又充当接收者。上面的类图只是为了方便理解。其实QueryStatementCallback和ExecuteStatementCallback是JdbcTemplate中方法的内部类。详见源码中的内容。部分源码分析StatementCallback接口:publicinterfaceStatementCallback{TdoInStatement(Statementsstmt)throwsSQLException,DataAccessException;}JdbcTemplate类:publicclassJdbcTemplateextendsJdbcAccessorimplementsJdbcOperations{//相当于调用者发出的命令@OverridepublicListquery(Stringsql,RowMapperT>rowMapper)throwsDataAccessException{returnquery(sql,newRowMapperResultSetExtractor(rowMapper));}//命令释放后,将具体命令发送给接收者执行@OverridepublicTquery(finalStringsql,finalResultSetExtractorrse)throwsDataAccessException{Assert.notNull(sql,"SQLmustnotbenull");Assert.notNull(rse,"ResultSetExtractormustnotbenull");if(logger.isDebugEnabled()){logger.debug("ExecutingSQLquery["+sql+"]");}//内部类,实现了StatementCallback,相当于一个具体的命令classQueryStatementCallbackimplementsStatementCallback,SqlProvider{@OverridepublicTdoInStatement(Statementsstmt)throwsSQLException{ResultSetrs=null;try{rs=stmt.executeQuery(sql);ResultSetrsToUse=rs;if(nativeJdbcExtractor!=null){rsToUse=nativeJdbcExtractor.getNativeResultSet(rs);}returnrse.extractData(rsToUse);}最后{JdbcUtils.closeResultSet(rs);}}@OverridepublicStringgetSql(){returnsql;}}returnexecute(newQueryStatementCallback());}//当接收者,命令真正的执行者@OverridepublicTexecute(StatementCallbackaction)throwsDataAccessException{Assert.notNull(action,"Callbackobjectmustnotbenull");Connectioncon=DataSourceUtils.getConnection(getDataSource());Statementstmt=null;try{ConnectionconToUse=con;if(this.nativeJdbcExtractor!=null&&this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()){conToUse=this.nativeJdbcExtractor.getNativeConnection(con);}stmt=conToUse.createStatement();applyStatementSettings(stmt);StatementstmtToUse=stmt;if(this.nativeJdbcExtractor!=null){stmtToUse=this.nativeJdbcExtractor.getNativeStatement(stmt);}Tresult=action.doInStatement(stmtToUse);处理警告(stmt);returnresult;}catch(SQLExceptionex){//尽早释放连接,避免潜在的连接池死锁//在异常翻译器尚未初始化的情况下.translate("StatementCallback",getSql(action),ex);}finally{JdbcUtils.closeStatement(stmt);DataSourceUtils.releaseConnection(con,getDataSource());}}}PS:以上代码提交在Github:https://github.com/Niuh-Study/niuh-designpatterns.git