考虑这样一种场景,某个方法需要完成某一个功能,完成这个功能的大部分步骤已经确定了,但可能有少量具体步骤无法确定,必须等到该方法执行时才可以确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要调用该方法时指定具体的处理行为。这个要求看起来有点奇怪:这个方法不仅要求传入参数可以变化,甚至要求方法执行体的代码也可以变化,难道我们能把“处理行为”作为一个参数传入该方法?
很遗憾,Java语言暂时还不支持将代码块作为参数传递给方法。
对于这样的要求,我们必须把“处理行为”作为参数传入该方法,而“处理行为”用编程来实现就是一段代码。那如何把这段代码传入某个方法呢?
因为Java不允许代码块单独存在,因此我们必须把该代码块封装成一个方法。在Java中,方法不能独立存在,类才是可以独立存在的,所以我们传入该方法的应该是一个对象,该对象通常是某个接口的匿名实现类的实例,该接口通常被称为命令接口,这种设计模式也被称为命令模式。
下面的程序先定义一个ProcessArray类,该类里包含一个each()方法用于处理数组,但具体如何处理暂时不能确定,所以each()方法里定义了一个Command参数:
1 2 3 4 5 |
|
上面的each方法有一个Command类型的cmd参数,这个Command接口用于定义一个process()方法,该方法用于封装对数组的“处理行为”,下面是Command接口的代码:
1 2 3 |
|
上面的Command接口里定义了一个process方法,这个方法用于封装“处理行为”,但这个方法没有方法体——因为现在还无法确定这个处理行为。
当主程序调用ProcessArray对象的each()方法来处理数组时,每次处理数组需要传入不同的“处理行为”——也就是要为each()方法传入不同的Command对象,不同的Command对象封装了不同的“处理行为”。下面是主程序调用ProcessArray对象each()方法的程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
正如上面的代码所示:程序两次调用ProcessArray对象的each()方法来处理数组对象,每次调用时传入不同的Command实现类的实例,不同的Command实例封装了不同的“处理行为”。运行上面的程序,可以看到以下结果:
上图显示了两次不同处理行为的执行结果,也就实现了process方法和“处理行为”的分离,两次不同的处理行为分别由两个不同的Command对象来提供。
理解了这个命令模式后,我们再看看Spring框架中HibernateTemplate的executeXxx()方法。这个方法弥补了HibernateTemplate的不足,该方法需要接受一个HibernateCallback接口,该接口的代码如下:
1 2 3 |
|
上面的HibernateCallback接口就是一个典型的Command接口,一个HibernateCallback对象封装了自定义的持久化处理。对Hibernate而言,大部分持久化操作都可以通过一个方法来实现,HibernateTemplate对象简化了Hibernate的持久化操作,但丢失了使用Hibernate持久化操作的灵活性。
通过HibernateCallback就可以弥补HibernateTemplate灵活性的不足,当调用HibernateTemplate的executeXxx()方法时,传入HibernateCallback对象的doInHibernate()方法就是自定义的持久化处理——即将自定义的持久化处理传入executeXxx()方法,如下面的代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
上面的程序中将自定义的方法传给HibernateTemplate,HibernateTemplate将直接使用这段代码来执行查询,并将查询结果作为executeFind()方法的返回值。这也是一个典型的命令模式的例子。