9.3.5 命令模式
考虑这样一种场景:某个方法需要完成某一个功能,完成这个功能的大部分步骤已经确定了,但可能有少量具体步骤无法确定,必须等到执行该方法时才可以确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要在调用该方法时指定具体的处理行为。
这个要求看起来有点奇怪:这个方法不仅要求参数可以变化,甚至要求方法执行体的代码也可以变化,需要能把“处理行为”作为一个参数传入该方法。
在某些编程语言(如Ruby、Perl等)中,确实允许传入一个代码块作为参数。从Java8开始支持的Lambda表达式也可以实现这种功能.
对于这样的需求,要求把”处理行为”作为参数传入该方法,而”处理行为”用编程来实现就是段代码。那如何把这段代码传入某个方法呢?在Java语言中,类才是一等公民,方法也不能独立存在,所以实际传入该方法的应该是一个对象,该对象通常是某个接口的匿名实现类的实例,该接口通常被称为命令接口,这种设计方式也被称为命令模式。
程序示例
| 1 | E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\Command | 
下面的程序先定义一个ProcessArray类,该类里包含一个each方法用于处理数组,但具体如何处理暂时不能确定,所以each()方法里定义了一个Command参数。
| 1 | public class ProcessArray | 
上面定义each()方法时,指定了一个Command形参,这个Command接口用于定义一个process()方法,该方法用于封装对数组的”处理行为”。下面是该Command接口代码。
| 1 | public interface Command | 
上面的Command接口里定义了一个process()方法,这个方法用于封装”处理行为”,但这个方法没有方法体—因为现在还无法确定这个处理行为。
下面是主程序调用ProcessArray对象each()方法的程序。
| 1 | public class CommandTest | 
正如上面的程序中两段粗体字代码所示,程序两次调用ProcessArray对象的each()方法来处理数组对象,每次调用each()方法时传入不同的Command匿名实现类的实例,不同的Command实例封装了不同的”处理行为”。
运行上面程序,将看到如下结果。
| 1 | 迭代输出目标数组的元素:3 | 
上面的运行结果显示了两次不同处理行为的结果,也就实现了process()方法和”处理行为”的分离,两次不同的处理行为分别由两个不同的Command对象来提供。
Java8新增了Lambda表达式功能,Java8允许使用Lambda表达式创建函数式接口的实例。
什么是函数式接口
所谓函数式接口,指的是只包含一个抽象方法的接口。上面程序中的Command接口就是函数式接口。因此CommandTest可改写为如下形式
| 1 | public class LambdaTest | 
理解了这个命令模式后,相信读者对Spring框架中HibernateTemplate的execute方法找到了一点感觉, HibernateTemplate使用了execute()方法弥补了HibernateTemplate的不足,该方法需要接受一个HibernateCallback接口,该接口的代码如下:
| 1 | // 定义一个`HibernateCallback`接口,该接口封装持久化处理行为 | 
上面的HibernateCallback接口就是一个典型的Command接口,一个HibernateCallback对象封装了自定义的持久化处理
对HibernateTemplate而言,大部分持久化操作都可通过一个方法来实现, HibernateTemplate对象简化了Hibernate的持久化操作,但丢失了使用Hibernate持久化操作的灵活性.
通过HibernateCallback就可以弥补HibernateTemplate灵活性不足的缺点,当调用HibernateTemplate的execute()方法时,传入Hibernate Callback对象的dolnhibernateo方法就是自定义的持久化处理—即将自定义的持久化处理传入了execute()方法。下面的代码片段使用Lambda表达式来实现该功能。
| 1 | List list=getHibernateTemplate() | 
上面程序中的粗体字代码块将直接传给HibernatTemplate,HibernatTemplate将直接使用该代码块来执行持久化查询,并将查询得到的结果作为execute()方法的返回值。
原文链接: 9.3.5 命令模式