一、引言:
路边有一个烤肉摊,有一位老板负责给客人烧烤食物。如果客人很少,老板都能准确记住大家的要求,随着客人的增多,要求越来越多,老板很难满足大家要求,老板应该怎么办?
首先说明为什么客人多了,老板满足不了大家的要求,因为“用程序猿的话说是因为:紧耦合”。松耦合就能很好的解决此问题,也就是本文介绍的命令模式。先给出紧耦合代码:
classPRogram
{staticvoidMain(string[] args)
{
Receiver receiver=newReceiver();
receiver.BakeChickenWing();
Console.Read();
}
}publicclassReceiver//boss{publicvoidBakeMutton()
{
Console.WriteLine("bake mutton");
}publicvoidBakeChickenWing()
{
Console.WriteLine("bake chickenwing");
}
}
View Code
二、定义:
命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销操作。
解释:将请求命令封装为一个对象,并用日志记录下来,其中它支持命令撤销功能。
三、UML图及基本代码:
基本代码:
classReceicer
{publicvoidAction()
{
Console.WriteLine("执行请求");
}
}abstractclassCommand
{protectedReceicer receiver;publicCommand(Receicer receiver)
{this.receiver =receiver;
}abstractpublicvoidExecute();
}classConcreteCommand : Command
{publicConcreteCommand(Receicer receiver)
:base(receiver)
{ }publicoverridevoidExecute()
{
receiver.Action();
}
}classInvoker
{privateCommand command;publicvoidSetCommand(Command command)
{this.command =command;
}publicvoidExecuteCommand()
{
command.Execute();
}
}
调用:
Receicer receiver =newReceicer();
Command command=newConcreteCommand(receiver);
Invoker invoker=newInvoker();
invoker.SetCommand(command);
invoker.ExecuteCommand();
解释:映射客人到酒店就餐。Receiver是命令的接收者,相当于酒店中的大厨;Command命令抽象类,ConcreteCommand是具体的命令,相当于点菜请求,命令中应该包含命令的接收者。Invoke是命令的传达者,相当于酒店的服务员,负责记录客人点菜请求,并传达给后厨。
四、举例说明:
学校高一学生进行军训,校长发布让学生跑10000米的命令。整个过程:校长发布命令给军训教官,教官传达校长命令给学生,学生接收到命令后执行相应的操作。在下面实例中,Receiver是学生,操作跑10000米的命令。Command是命令,必需知道命令的接收者。Drillmaster是教官,教官必需知道命令是什么,并传达命令。
//校长发布学生跑1000米的命令,教官传达此命令给学生,学生是命令的接收者//客户端:校长,命令的发出者必须知道具体的命令、接受者、传达命令者classProgram
{staticvoidMain(string[] args)
{
Receiver receiver=newReceiver();
Command command=newConcreteCommand(receiver);
Drillmaster drillmaster=newDrillmaster(command);
drillmaster.ExecuteCommand();
Console.Read();
}
}//命令接收者publicclassReceiver
{publicvoidRun1000Meters()
{
Console.WriteLine("跑10000米");
}
}//抽象命令publicabstractclassCommand
{protectedReceiver receiver;publicCommand(Receiver receiver)
{this.receiver =receiver;
}publicabstractvoidAction();
}//具体的命令,必须知道命令接受者publicclassConcreteCommand : Command
{publicConcreteCommand(Receiver receiver)
:base(receiver)
{ }publicoverridevoidAction()
{
receiver.Run1000Meters();
}
}//教官:命令的传达者,负责调用命令对象的方法来保证命令执行publicclassDrillmaster
{publicCommand command;publicDrillmaster(Command command)
{this.command =command;
}publicvoidExecuteCommand()
{
command.Action();
}
}
View Code
五、解决引言中的问题
客人点菜命令的接收者,老板(大厨):
publicclassReceiver
{publicvoidBakeMutton()
{
Console.WriteLine("bake mutton");
}publicvoidBakeChickenWing()
{
Console.WriteLine("bake chickenwing");
}
}
点菜命令:
publicabstractclassCommand
{protectedReceiver receiver;publicCommand(Receiver receiver)
{this.receiver =receiver;
}abstractpublicvoidExecute();
}classConcreteCommand1 : Command
{publicConcreteCommand1(Receiver receiver)
:base(receiver)
{ }publicoverridevoidExecute()
{
receiver.BakeMutton();
}
}classConcreteCommand2 : Command
{publicConcreteCommand2(Receiver receiver)
:base(receiver)
{ }publicoverridevoidExecute()
{
receiver.BakeChickenWing();
}
}
增加一个服务生,负责记录客人点菜命令,并传达给大厨。点菜命令可以进行增加或取消操作。
publicclassInvoker
{privateIList<Command> commands =newList<Command>();publicvoidAddCommand(Command command)
{
commands.Add(command);
Console.WriteLine("增加订单"+command.ToString());
}publicvoidCancelCommand(Command command)
{
commands.Remove(command);
Console.WriteLine("取消订单"+command.ToString());
}publicvoidExecuteCommand()
{foreach(Command commandincommands)
{
command.Execute();
}
}
}
客户端调用:
Receiver receiver =newReceiver();
Command command1=newConcreteCommand1(receiver);
Command command2=newConcreteCommand2(receiver);
Invoker invoker=newInvoker();
invoker.AddCommand(command1);
invoker.AddCommand(command2);
invoker.CancelCommand(command1);
invoker.ExecuteCommand();
六、优缺点及适用场景
优点:
比较容易的设计一个命令队列;在需要的情况下,可以比较容易的将命令计入日志;允许接收请求的一方决定是否要否决请求;比较容易的对命令实现撤销和重做;由于加入新的具体命令类不影响其他的类,因此增加新的具体命令类比较容易。
缺点:
可能会导致系统具有过多的具体命令类。
适用场景:
个人理解:基本上能表现其优点的地方都可以使用命令模式。