Head First设计模式 -- 命令模式

命令模式

定义:将请求封装成对象,以便使用不同的请求队列或者日志来参数化其它对象。

 命令模式把发出命令和执行命令的责任分隔开,委派给不同的对象,减少之间的耦合。允许请求方与发送方独立开来,请求方并不需要知道接收请求的方的接口。

也就是说行为请求者与行为实现者之间呈现的松耦合


        1、以餐厅点餐为例,服务员不需要知道客户点的是什么,他只要负责将菜单交给厨师就ok了,下面,我们利用一个打开电灯为例来说明简单的命令模式:

    //首先我们来看一个电灯的例子
    class Light
    {
        String strDescription = "";
        public Light()
        { }
        public Light(String strDesc)
        {
            strDescription = strDesc;
        }
        public void on()
        {
            Console.WriteLine(strDescription + " Light is on");
        }
        public void off()
        {
            Console.WriteLine(strDescription + " light is off");
        }
    }
    interface Command
    {
        void execute();
        //public void on();
    }

    //假设要打开电灯的命令。使用on与off
    class LightOnCommand : Command
    {
        Light light;
        public LightOnCommand(Light light)
        {
            this.light = light;
        }

        public void execute()
        {
            light.on();
        }
        public void undo()//撤销的命令与执行的命令正好相反
        {
            light.off();
        }
    }

    class LightoffCommand : Command
    {
        Light light;
        public LightoffCommand(Light light)
        {
            this.light = light;
        }
        public void execute()
        {
            light.off();
        }
        public void undo()
        {
            light.on();
        }
    }

    class SimpleRemoteControl
    {
        public Command slot;
        public SimpleRemoteControl() { }
        public void setCommand(Command command)
        {
            slot = command;
        }
        public void buttonWasPressed()
        {
            slot.execute();
        }
    }

      上面是调用的方式,从中我们可以看见远程控制对象在选择了操作对象之后,只要下达命令,具体的对象就能够正确的执行。

     UML图为:


 

     SimpleControl内设置一个Command对象,就能够进行操作。

 

   2、那么能不能实现一组动作呢,进一步的根据不同的装备但是客户无需知道具体的执行步骤。

//下面对一个遥控器进行扩展,假设遥控器中有很多的插槽,每个的按键都对应着不同的功能。
    class NoCommand : Command
    {
        public void execute() { }
    }
   
    class RemoteControl
    {
        Command[] onCommands;
        Command[] offCommands;//为了简略,只控制两个电器
        public RemoteControl()
        {
            onCommands = new Command[3];
            offCommands = new Command[3];
            Command noCommand = new NoCommand();
            for (int i = 0; i < 2; i++)
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        //此函数接收不同的插槽中的打开与关闭的命令
        public void setCommand(int slot, Command onCommand, Command offCommand)
        {
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
        public void onButtonWasPushed(int slot)
        {
            if (onCommands[slot] != null)
            {
                onCommands[slot].execute();
            }
        }
        public void offButtonWasPushed(int slot)
        {
            if (offCommands[slot] != null)//检查插槽是否加载了命令。
            {
                offCommands[slot].execute();
            }
        }

        public String toString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("\n-----Remote Control -----\n");
            for (int i = 0; i < onCommands.Length; i++)
            {
                stringBuilder.Append("\n[slot" + i + "]  " + onCommands[i].GetType().Name + "  " + offCommands[i].GetType().Name);
            }
            return stringBuilder.ToString();
        }
    }



    //添加一个音响类
    class Stereo
    {
        String strDescription = "";
        public Stereo() { }
        public Stereo(String strDescription)
        {
            this.strDescription = strDescription;
        }

        public void on()
        {
            Console.WriteLine(strDescription + " stereo is on");
        }
        public void off()
        {
            Console.WriteLine(strDescription + " stereo is off");
        }
        public void setCd()
        {
            Console.WriteLine(strDescription + " stereo is set for CD input");

        }
        public void setDvd() { }
        public void setRadio() { }
        public void setVolume()
        {
            Console.WriteLine(strDescription + " stereo is set for Volume ");
        }
    }

    //相应的加入音响的命令类
    class StereoOnCommand : Command
    {
        Stereo stero;
        public StereoOnCommand(Stereo stero)
        {
            this.stero = stero;
        }
        public void execute()
        {
            stero.on();
            stero.setCd();//打开CD
            stero.setVolume();//调节音量
        }
    }
    class StereoOffCommand : Command
    {
        Stereo stereo;
        public StereoOffCommand(Stereo stereo)
        {
            this.stereo = stereo;
        }
        public void execute()
        {
            stereo.off();
        }
    }

  //遥控器的例子
            RemoteControl remoteControl = new RemoteControl();
            //将所有的装置创建在合适的位置
            Light livingRoomLight = new Light("Living Room");//卧室的灯
            Light kitchenLight = new Light("Kitchen");//厨房的灯
            Stereo stereo = new Stereo("Living Room");
            //创建所有的电灯命令对象
            LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
            LightoffCommand livingRoomLightOff = new LightoffCommand(livingRoomLight);
            LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
            LightoffCommand kitchenLightOff = new LightoffCommand(kitchenLight);
            //创建音响的开关命令
            StereoOnCommand stereoOn = new StereoOnCommand(stereo);
            StereoOffCommand stereoOff = new StereoOffCommand(stereo);

            remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
            remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
            remoteControl.setCommand(2, stereoOn, stereoOff);

            Console.WriteLine(remoteControl.toString());
            Console.WriteLine();

            //发出命令
            remoteControl.onButtonWasPushed(0);
            remoteControl.onButtonWasPushed(1);
            remoteControl.onButtonWasPushed(2);

            Console.WriteLine();
            remoteControl.offButtonWasPushed(0);
            remoteControl.offButtonWasPushed(1);
            remoteControl.offButtonWasPushed(2);

    对应的UML图为:

    

运行结果为:


     可见添加的控制类实现了对所有电器的封装,那么命令者模式有什么好处呢?

     主要的为将执行动作给独立出来,封装下面的接口,因为执行动作对象并不是操作的对象,因此,利用Command这个中间类来解耦合。现在的疑问是,为什么不直接让命令者去做这些事情呢,而是分开添加装置与执行命令这两个方法。个人的理解是,通过这种方法你可以对这些对象进行更多的操作,比如你要对它们的动作进行撤销,而不仅仅是执行。书中的解释是:尽量设计“傻瓜”命令对象,它只能理解调用接受者的一个行为。

     那么现在还存在的一个问题是,如何能够实现撤销的动作,其实主要的命令模式的本质已经讲完了。在于通过命令对象之间的沟通。在实际应用中有没有这种命令呢。书中谈及工作队列,让我想起MFC中的消息队列这个机制,同样它也是将所有的消息放在队列中,通过不断的获取消息->翻译消息->执行消息。那么这个执行消息对于消息队列来说,它是不能够获知的。因为消息包括窗口消息、控件通知、命令消息,不同的执行内容不一。作为消息队列,只需触发该消息即可。

 

3、撤销动作

    
  interface Command1
    {
        void execute();
        void undo();
    }
    class NoCommand1 : Command1
    {
        public void execute() { }
        public void undo() { }
    }
    class RemoteControlWithUndo
    {
        Command1[] onCommands;
        Command1[] offCommands;
        Command1 undoCommand;
        int slotNum;
        public RemoteControlWithUndo(int slotNum)
        {
            onCommands = new Command1[slotNum];
            offCommands = new Command1[slotNum];
            Command1 noCommand = new NoCommand1();

            for (int i = 0; i < slotNum; i++)
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
            undoCommand = noCommand;
        }

        public void setCommand(int slot, Command1 onCommand, Command1 offCommand)
        {
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
        //关键思想是设置了一个撤销的命令,得到当前操作的插槽号,据此执行撤销命令
        public void onButtonWasPushed(int slot)
        {
            onCommands[slot].execute();
            undoCommand = offCommands[slot];
        }

        public void offButtonWasPushed(int slot)
        {
            offCommands[slot].execute();
            undoCommand = onCommands[slot];
        }
        public void undoButtonWasPushed()
        {
            undoCommand.execute();
        }
    }

    //下面我们测试一个电扇,因为电扇有很多的状态,如何实现它的撤销功能呢??

    class CeilingFun
    {
        public const int HIGH = 3;
        public const int MEDIUM = 2;
        public const int LOW = 1;
        public const int OFF = 0;
        String location;
        int speed;

        public CeilingFun(String location)
        {
            this.location = location;
            speed = OFF;
        }

        public void high()
        {
            speed = HIGH;
            Console.WriteLine(location + " Ceiling fan is on high");
        }
        public void medium()
        {
            speed = MEDIUM;
            Console.WriteLine(location + " Ceiling fan is on medium");
        }
        public void low()
        {
            speed = LOW;
            Console.WriteLine(location + " Ceiling fan is on low");
        }
        public void off()
        {
            speed = OFF;
            Console.WriteLine(location + " Ceiling fan is on off");
        }
        //获取当前的速度
        public int getSpeed()
        {
            return speed;
        }
    }
    class CeilingFanHighCommand : Command1
    {
        CeilingFun ceilingFun;
        int prevSpeed;
        public CeilingFanHighCommand(CeilingFun ceilingFun)
        {
            this.ceilingFun = ceilingFun;
        }
        public void execute()
        {
            prevSpeed = ceilingFun.getSpeed();
            ceilingFun.high();
        }
        public void undo()
        {
            if (prevSpeed == CeilingFun.HIGH)
            {
                ceilingFun.high();
            }
            else if (prevSpeed == CeilingFun.MEDIUM)
            {
                ceilingFun.medium();
            }
            else if (prevSpeed == CeilingFun.LOW)
            {
                ceilingFun.low();
            }
            else if (prevSpeed == CeilingFun.OFF)
            {
                ceilingFun.off();
            }
        }
    }
    class CeilingFanMediumCommand : Command1
    {
         CeilingFun ceilingFun;
        int prevSpeed;
        public CeilingFanMediumCommand(CeilingFun ceilingFun)
        {
            this.ceilingFun = ceilingFun;
        }
        public void execute()
        {
            prevSpeed = ceilingFun.getSpeed();
            ceilingFun.medium();
        }
        public void undo()
        {
            if (prevSpeed == CeilingFun.HIGH)
            {
                ceilingFun.high();
            }
            else if (prevSpeed == CeilingFun.MEDIUM)
            {
                ceilingFun.medium();
            }
            else if (prevSpeed == CeilingFun.LOW)
            {
                ceilingFun.low();
            }
            else if (prevSpeed == CeilingFun.OFF)
            {
                ceilingFun.off();
            }
        }
    }
    public class RemoteLoader2
    {
        public static void test()
        {
            RemoteControlWithUndo remoteControl = new RemoteControlWithUndo(1);
            CeilingFun ceilingFun = new CeilingFun("Living Room");
            CeilingFanHighCommand ceilingFunHighCommand = new CeilingFanHighCommand(ceilingFun);
            CeilingFanMediumCommand ceilingFunMediumCommand = new CeilingFanMediumCommand(ceilingFun);
            remoteControl.setCommand(0, ceilingFunHighCommand, ceilingFunMediumCommand);
            remoteControl.onButtonWasPushed(0);
            remoteControl.offButtonWasPushed(0);
            remoteControl.undoButtonWasPushed();
        }

    }
    //如果想要一键控制所有的电器,那么添加一个类,将所有的电器置于容器中


    运行结果为:



小结:

命令模式为交互对象之间的松耦合而设计。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值