设计模式 -- 命令模式(command)

 

1、什么是command命令模式?

    command命令模式是“封装方法调用”的一个模式,通过封装方法调用,把运算块封装成形。那么调用此运算的对象就不必深究事情是如何进行的,只需要知道可以使用这个包装成型的方法来完成它就可以了。因为它把运算封装,而不关心具体对象和实现,因此命令模式在我们的系统中有很多应用,如队列请求,日志请求,撤消操作等等。

    “封装方法调用”也可以这样理解,命令模式把“命令的请求者”和“命令的执行者”解耦。这样一来,当命令请求者在请求一个命令时不必关心执行此命令的是哪个对象,这个对象怎么执行这个命令这些细节问题。要实现这两者之间的解耦,必然的要多出一个“中间处理者”——命令,我们就把方法调用封装在这个结构里。更通俗的来讲一个例子,比如你去餐馆吃饭,服务员招待你,你点你要的东西,服务员会把这些内容记录成一个单子,这个单子就是命令,为什么呢?因为大厨必须根据这个单子来做菜,否则你是不会满意的。如果你认同我这种说法,那么就说明,你承认了“命令请求者”(你)和“命令执行者”之间解耦了。因为这之间,你不知道大厨是谁,也不知道他是怎么做的菜。

命令模式 -- 将请求封装成对象,这可以让你使用不同的请求,队列,或者日志请求来参数化其他对象,命令模式支持撤销操作。

2、如何实现“命令模式”模式?

     实现命令模式,需要有一个“命令”接口(command),这个接口定义了一些列的命令,比如execute,undo等。你可以实现这个接口,那么你就有了一些具体的命令了。这个“命令”(command )接口就是我刚才说的“中间处理者”。命令的请求者在请求命令时,只需要指明需要的具体命令,而这个具体命令就必须维护自己的命令执行者,这样三者之间就发生了联系。看看下面的类图:

来解释这个类图:

Command:(可以是接口或者抽象类)为所有命令声明了接口。调用命令对象的execute方法就可以让接受者进行相关动作。

Concretecommand:定义了动作和接收者之间的绑定关系。调用者只要调用execute就可以发出请求,然后由Concretecommand调用接收者的一个或多个动作。

Client:(通常是我们的测试程序)负责创建一个ConcreteCommand,并设置其接收者。

Invoker:调用者,他要维护自己的命令对象,并在某个时间点调用命令对象的execute方法,将请求付诸实行。

Receiver:接收者,它知道如何进行必要的工作来实现这个请求。

3、具体情境分析

     遥控器有很多按钮,我们打算将遥控器的每个按钮对应到一个命令,这样让遥控器成为“调用者”。当按下按钮,相应命令对象的execute方法会执行被调用,其结果就是,接收者(如电灯,车库门,吊扇)的动作被调用。

     类图:

    接收者类图命令调用者类

 

代码:

//命令接收者 -- 天花板
    public class CeilingFan
    {
        public string position;

        public CeilingFan(string aPosition)
        {
            this.position = aPosition;
        }

        public void on()
        {
            Console.WriteLine("the ceilingfan is on");
        }

        public void off()
        {
            Console.WriteLine("the ceilingfan is off");
        }
 
    }
//命令接收者 -- 电灯类
    public class Light
    {
        public string position;//指明灯的位置

        public Light(string aName)
        {
            this.position = aName;
        }

        public void on()
        {
            Console.WriteLine("电灯打开了");
        }

        public void off()
        {
            Console.WriteLine("电灯关上了");
        }
    }
//命令接收者 -- 车库门
    public class GarageDoor
    {
        public void up()
        {
            Console.WriteLine("门向上开启");
        }

        public void down()
        {
            Console.WriteLine("门向下关闭");
        }

        public void stop()
        {
            Console.WriteLine("门停止");
        }
        public void lightOn()
        {
            Console.WriteLine("打开车库门");
        }
        public void lightOff()
        {
            Console.WriteLine("车库门关上");
        }
    }
//命令接收者 -- 音响类
    public class Stereo
    {
        public string position;

        public Stereo(string aposition)
        {
            this.position = aposition;
        }

        public void on()
        {
            Console.WriteLine("音响打开了");
        }

        public void off()
        {
            Console.WriteLine("音响关上了");
        }

        public void setCD()
        {
            Console.WriteLine("CD就绪");
        }

        public void setVolume(int num)
        {
            Console.WriteLine("音响有");
            Console.WriteLine(num);
            Console.WriteLine("首歌");
        }

        public void setRadio()
        {
            Console.WriteLine("音响录音");
        }
    }
//命令请求者 -- 遥控器
    public class RemoteControl
    {
        public Command[] onCommands;//7个开命令
        public Command[] offCommands;//7个关命令
        public Command undoCommand;//前一个命令将被记录在这里

        public RemoteControl()
        {
            onCommands = new Command[7];
            offCommands = new Command[7];

            Command noCommand = new NoCommand();
            for (int i = 0; i < 7;i ++ )
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }

            undoCommand = noCommand;//一开始没有所谓的“前一个”命令
        }

        //参数:插槽位置 开命令 关命令。这些命令记录在开关数组中对应插槽的位置上
        public void setCommand(int slot, Command onComand, Command offCommand)
        {
            onCommands[slot] = onComand;
            offCommands[slot] = offCommand;
        }

        public void onButtonWasPushed(int slot)
        {
            if (onCommands[slot] != null)
            {
                onCommands[slot].execute();
                undoCommand = onCommands[slot];
            }  
        }

        public void offButtonWasPushed(int slot)
        {
            if (offCommands[slot] != null)
            {
                offCommands[slot].execute();
                undoCommand = offCommands[slot];
            }      
        }

        public void undoButtonWasPressed()
        {
            undoCommand.undo();
        }
        public string toString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("\n------ Remote  Control ------\n");
            for (int i = 0; i < 7;i++ )
            {
                stringBuilder.Append("[slot" + i + "]" + onCommands[i] + "      " + offCommands[i] + "\n");
            }
            return stringBuilder.ToString();
        }
    }
//命令接口
    public interface Command
    {
        void execute();
        void undo();//撤销操作
    }

//打开电灯命令类
    public class LightOnCommand:Command
    {
        Light light;

        public LightOnCommand(Light alight)
        {
            this.light = alight;
        }

        public void execute()
        {
            light.on();
        }

        public void undo()
        {
            light.off();
        }
    }

//关闭电灯命令类
    public class LightOffCommand:Command
    {
        public Light light;

        public LightOffCommand(Light alight)
        {
            this.light = alight;
        }

        public void execute()
        {
            light.off();
        }

        public void undo()
        {
            light.on();//execute是关闭电灯,那么undo就是打开电灯
        }
    }

其他的命令类都是大同小异的,在此省略了。

测试代码:

static void Main(string[] args)
        {
            //命令调用者
            RemoteControl remoteControl = new RemoteControl();
            //命令接收者
            Light livingRoomLight = new Light("Living  Room");
            Light kitchenLight = new Light("Kitchen");
            CeilingFan ceilingfan = new CeilingFan("Living  Room");
            GarageDoor garagedoor = new GarageDoor();
            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);

            CeilingFanOnCommand ceilingFanOn = new CeilingFanOnCommand(ceilingfan);
            CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingfan);

            GarageDoorOpenCommand garageDoorOpen = new GarageDoorOpenCommand(garagedoor);
            GarageDoorDownCommand garageDoorDowm = new GarageDoorDownCommand(garagedoor);

            StereoOnWithCDCommand stereoOnWithCD = new StereoOnWithCDCommand(stereo);
            StereoOffCommand stereoOff = new StereoOffCommand(stereo);

            //将命令对应到调用者的接口上
            remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
            remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
            remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
            remoteControl.setCommand(3, stereoOnWithCD, stereoOff);
            remoteControl.setCommand(4, garageDoorOpen, garageDoorDowm);

            string str = remoteControl.toString();
            Console.WriteLine(str);

            remoteControl.onButtonWasPushed(0);
            remoteControl.offButtonWasPushed(0);

            remoteControl.onButtonWasPushed(1);
            remoteControl.offButtonWasPushed(1);

            remoteControl.onButtonWasPushed(2);
            remoteControl.offButtonWasPushed(2);

            remoteControl.onButtonWasPushed(3);
            remoteControl.offButtonWasPushed(3);

            remoteControl.onButtonWasPushed(4);
            remoteControl.offButtonWasPushed(4);

            //以下是撤销操作
            Console.WriteLine("------------------\n");
            remoteControl.undoButtonWasPressed();
            remoteControl.offButtonWasPushed(4);
            remoteControl.onButtonWasPushed(4);
            remoteControl.undoButtonWasPressed();

            Console.ReadKey();


        }
    }

呵呵,好了,也许你发下了,我们这里的撤销操作只是一步撤销,而往往我们希望能够连续撤销而回到很久以前的一个状态去。实现多层次撤销,我们可以使用堆栈记录操作的每一个命令,然后,不管什么时候按下按钮,你都可以从堆栈中取出最上层命令,然后调用它的undo()方法。我们可以使用宏命令,将命令放在一个一个集合里,这样就可实现这个遥控器的“Party”模式,开让一个按钮一次性控制多个命令。

posted on 2013-04-10 17:10  雨过晴空  阅读(263)  评论(0编辑  收藏  举报

导航