经典设计模式——命令模式

    考虑这样一种场景,某个方法需要完成某一个功能,完成这个功能的大部分步骤已经确定了,但可能有少量具体步骤无法确定,必须等到该方法执行时才可以确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要调用该方法时指定具体的处理行为。这个要求看起来有点奇怪:这个方法不仅要求传入参数可以变化,甚至要求方法执行体的代码也可以变化,难道我们能把“处理行为”作为一个参数传入该方法?

    很遗憾,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()方法的返回值。这也是一个典型的命令模式的例子。

时间: 2024-08-01 15:52:45

经典设计模式——命令模式的相关文章

解读设计模式----命令模式(Command Pattern)

本文与作者原文有一定的偏差,其中加入了一部分是个人看法,详细请查看作者原文.*** 原文连接http://www.dofactory.com/Patterns/PatternCommand.aspx 命令模式意图: GOF 在<设计模式>一书中阐述其意图:"将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可取消的操作."这里所谓的"不同的请求"也既意味着请求可能发生的变化,是一个可能扩展的功能点. 命令

[Head First设计模式]餐馆中的设计模式——命令模式

系列文章 [Head First设计模式]山西面馆中的设计模式--装饰者模式 [Head First设计模式]山西面馆中的设计模式--观察者模式 [Head First设计模式]山西面馆中的设计模式--建造者模式 [Head First设计模式]饺子馆(冬至)中的设计模式--工厂模式 [Head First设计模式]一个人的平安夜--单例模式 [Head First设计模式]抢票中的设计模式--代理模式 [Head First设计模式]面向对象的3特征5原则 [Head First设计模式]鸭子

PHP设计模式——命令模式

声明:本系列博客参考资料<大话设计模式>,作者程杰.             命令模式:在软件系统中,"行为请求者"与"行为实现者"通常呈现一种"紧耦合".但在某些场合,比如要对行为进行"记录.撤销/重做.事务"等处理,这种无法抵御变化的紧耦合是不合适的.在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,实现二者之间的松耦合.这就是命令模式.  

.NET设计模式-命令模式(Command Pattern)

命令模式(Command Pattern) --.NET设计模式系列之十七 TerryLee,2006年7月 概述 在软件系统中,"行为请求者"与"行为实现者"通常呈现一种"紧耦合".但在某些场合,比如要对行为进行"记录.撤销/重做.事务"等处理,这种无法抵御变化的紧耦合是不合适的.在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合[李

iOS设计模式 - 命令模式

前言: 命令对象封装了如何对目标执行指令的信息,因此客户端或调用者不必了解目标的任何细节,却仍可以对他执行任何已有的操作.通过把请求封装成对象,客 户端可 以把它参数化并置入队列或日志中,也能够支持可撤销操作.命令对象将一个或多个动作绑定到特定的接收器.命令模式消除了作为对象的动作和执行它的接收器之 间的绑定. 正文内容大纲: 1.电视机.遥控器与接收器之间的关系 2.改变一个视图的明暗程度(未使用命令模式) 3.命令模式介绍 4.改变一个视图的明暗程度(使用命令模式) 5.附录 1.电视机.遥

14、Python与设计模式--命令模式

一.饭店点餐系统 又是一个点餐系统(原谅作者的吃货属性).不过这次的点餐系统是个饭店的点餐系统.饭店的点餐系统有什么不同嘛?大伙想想看,在大多数饭店中,当服务员已经接到顾客的点单,录入到系统中后,根据不同的菜品,会有不同的后台反应.比如,饭店有凉菜间.热菜间.主食间,那当服务员将菜品录入到系统中后,凉菜间会打印出顾客所点的凉菜条目,热菜间会打印出顾客所点的热菜条目,主食间会打印出主食条目.那这个系统的后台模式该如何设计?当然,直接在场景代码中加if-else-语句判断是个方法,可这样做又一次加重

Java设计模式--命令模式

命令模式(别名:动作,事物) 为系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. Command Pattern(Another name:Action,Transaction) Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and s

设计模式之禅之设计模式-命令模式

一:命令模式的定义        --->命令模式是一个高内聚的模式        --->将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能.        --->命令模式的角色                ● Receive接收者角色==>该角色就是干活的角色,命令传递到这里是应该被执行的                ● Command命令角色==>需要执行的所有命令都在这里声明         

经典设计模式——桥接模式

    桥接模式是一种结构型模式,它主要应对的是:由于实际的需要,某个类具有两个或以上维度的变化,如果只是使用继承,将无法实现这种需要,或者是的设计变得更加臃肿.     举例来说,假设现在我们需要为某个餐厅制造菜单,餐厅供应牛肉面.猪肉面--,而且顾客可以根据自己的口味选择是否添加辣椒.此时就产生了一个问题,我们如何来应对这种变化(这已经是两个维度的变化了,一个维度是面里面加的什么肉,另一个维度是加不加辣椒)?我们是否需要定义辣椒牛肉面.无辣牛肉面.辣椒猪肉面.无辣猪肉面4(2^2)个子类?如