考虑这样一种场景,某个方法需要完成某一个功能,完成这个功能的大部分步骤已经确定了,但可能有少量具体步骤无法确定,必须等到该方法执行时才可以确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要调用该方法时指定具体的处理行为。这个要求看起来有点奇怪:这个方法不仅要求传入参数可以变化,甚至要求方法执行体的代码也可以变化,难道我们能把“处理行为”作为一个参数传入该方法?
很遗憾,Java语言暂时还不支持将代码块作为参数传递给方法。
对于这样的要求,我们必须把“处理行为”作为参数传入该方法,而“处理行为”用编程来实现就是一段代码。那如何把这段代码传入某个方法呢?
因为Java不允许代码块单独存在,因此我们必须把该代码块封装成一个方法。在Java中,方法不能独立存在,类才是可以独立存在的,所以我们传入该方法的应该是一个对象,该对象通常是某个接口的匿名实现类的实例,该接口通常被称为命令接口,这种设计模式也被称为命令模式。
下面的程序先定义一个ProcessArray类,该类里包含一个each()方法用于处理数组,但具体如何处理暂时不能确定,所以each()方法里定义了一个Command参数:
1
2
3
4
5
|
public
class
ProcessArray {
pubic
void
each(
int
[] targetArray, Command cmd) {
cmd.process(targetArray);
}
}
|
上面的each方法有一个Command类型的cmd参数,这个Command接口用于定义一个process()方法,该方法用于封装对数组的“处理行为”,下面是Command接口的代码:
1
2
3
|
public
interface
Command {
public
void
process(
int
[] targetArray);
}
|
上面的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
|
public
class
CommandTest {
public
static
void
main(String args[]) {
ProcessArray processArray =
new
ProcessArray();
int
[] target = {
1
,
2
,
3
,-
4
};
//第一次处理,其处理行为取决于Command对象
processArray.each(target,
new
Command() {
@Override
public
void
process(
int
[] targetArray) {
int
sum =
0
;
for
(
int
i:targetArray) {
sum += i;
}
System.out.println(
"数组元素的和为:"
+ sum);
}
});
System.out.println(
"================================="
);
//第二次处理,其处理行为取决于Command对象
processArray.each(target,
new
Command() {
@Override
public
void
process(
int
[] targetArray) {
int
max =
0
;
for
(
int
i:targetArray) {
if
( max < i) {
max = i;
}
}
System.out.println(
"数组元素的最大值为:"
+ max);
}
});
}
}
|
正如上面的代码所示:程序两次调用ProcessArray对象的each()方法来处理数组对象,每次调用时传入不同的Command实现类的实例,不同的Command实例封装了不同的“处理行为”。运行上面的程序,可以看到以下结果:
上图显示了两次不同处理行为的执行结果,也就实现了process方法和“处理行为”的分离,两次不同的处理行为分别由两个不同的Command对象来提供。
理解了这个命令模式后,我们再看看Spring框架中HibernateTemplate的executeXxx()方法。这个方法弥补了HibernateTemplate的不足,该方法需要接受一个HibernateCallback接口,该接口的代码如下:
1
2
3
|
public
interface
HibernateCallback {
Object doInHibernate(Session session);
}
|
上面的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
|
List list = getHibernateTemplate().executeFind(
new
HibernateCallback(){
//实现HibernateCallback接口的方法
@Override
public
Object doInHibernate(Session session) {
//执行自己的代码
List result = session.createQuery(hql)
.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return
result;
}
});
|
上面的程序中将自定义的方法传给HibernateTemplate,HibernateTemplate将直接使用这段代码来执行查询,并将查询结果作为executeFind()方法的返回值。这也是一个典型的命令模式的例子。