介绍
命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
结构图
角色
Receive接收者角色
该角色就是干活的角色,命令传递到这里是应该被执行的。
Command命令角色
需要执行的所有的调用者角色
Invoker调用者角色
接收到命令,并执行命令。
基本代码
客户端代码,创建一个具体命令对象并设定它的接收者
static void Main(string[] args)
{
Receiver r = new Receiver();
Command c = new ConcreteCommand(r);
Invoker i = new Invoker();
i.SetCommand(c);
i.ExecuteCommand();
Console.Read();
}
Command类,用来声明执行操作的接口
abstract class Command
{
protected Receiver receiver;
public Command(Receiver receiver)
{
this.receiver = receiver;
}
abstract public void Execute();
}
ConcreteCommand类,将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute
class ConcreteCommand : Command
{
public ConcreteCommand(Receiver receiver)
:
base(receiver) { }
public override void Execute()
{
receiver.Action();
}
}
Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者
class Receiver
{
public void Action()
{
Console.WriteLine("执行请求!");
}
}
Invoker类,要求该命令执行这个请求
class Invoker
{
private Command command;
public void SetCommand(Command command)
{
this.command = command;
}
public void ExecuteCommand()
{
command.Execute();
}
}
例子
大鸟对小菜说肚子饿了,走,我请你吃烤串。
此时老板烤的第一批肉串好了。“老板,我要两串”、“老板,我要三串不辣的”、“老板。我刚才已经给钱了”。旁边的人七嘴八舌的叫开了,场面有点混乱,以致于老板已经产生错误的判断了,造成分发错误,收钱错误。
这时大鸟对小菜说,走吧我们还是换一家吧,于是大鸟和小菜走到了一家烤肉店。
服务员,我们要十串羊肉串、两串鸡翅、两瓶啤酒。
这时服务员说:“鸡翅没有了,要点别的吧”
“那就来四串板筋,微辣啊”
上面的例子是两种不同的经营方式,那么如果转换成程序是怎样的呢?
第一种的结构图(烧烤摊)
客户端代码
static void Main(string[] args)
{
Barbecuer boy = new Barbecuer();
boy.BakeMutton();
boy.BakeMutton();
boy.BakeMutton();
boy.BakeChickenWing();
boy.BakeMutton();
boy.BakeMutton();
boy.BakeChickenWing();
Console.Read();
}
烤肉串者
public class Barbecuer
{
//烤羊肉串
public void BakeMutton()
{
Console.WriteLine("烤羊肉串!");
}
//烤鸡翅
public void BakeChickenWing()
{
Console.WriteLine("烤鸡翅!");
}
}
效果
第二种结构图(烧烤店)
客户端代码
static void Main(string[] args)
{
//开店前的准备
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter();
//开门营业 顾客点菜
girl.SetOrder(bakeMuttonCommand1);
girl.SetOrder(bakeMuttonCommand2);
girl.SetOrder(bakeChickenWingCommand1);
//点菜完闭,通知厨房
girl.Notify();
Console.Read();
}
服务员
public class Waiter
{
private IList<Command> orders = new List<Command>();
//设置订单
public void SetOrder(Command command)
{
if (command.ToString() == "命令模式.BakeChickenWingCommand")
{
Console.WriteLine("服务员:鸡翅没有了,请点别的烧烤。");
}
else
{
orders.Add(command);
Console.WriteLine("增加订单:" + command.ToString() + " 时间:" + DateTime.Now.ToString());
}
}
//取消订单
public void CancelOrder(Command command)
{
orders.Remove(command);
Console.WriteLine("取消订单:" + command.ToString() + " 时间:" + DateTime.Now.ToString());
}
//通知全部执行
public void Notify()
{
foreach (Command cmd in orders)
{
cmd.ExcuteCommand();
}
}
}
抽象命令
public abstract class Command
{
protected Barbecuer receiver;
public Command(Barbecuer receiver)
{
this.receiver = receiver;
}
//执行命令
abstract public void ExcuteCommand();
}
烤羊肉串命令
class BakeMuttonCommand : Command
{
public BakeMuttonCommand(Barbecuer receiver)
: base(receiver)
{ }
public override void ExcuteCommand()
{
receiver.BakeMutton();
}
}
烤鸡翅命令
class BakeChickenWingCommand : Command
{
public BakeChickenWingCommand(Barbecuer receiver)
: base(receiver)
{ }
public override void ExcuteCommand()
{
receiver.BakeChickenWing();
}
}
烤肉串者
public class Barbecuer
{
public void BakeMutton()
{
Console.WriteLine("烤羊肉串!");
}
public void BakeChickenWing()
{
Console.WriteLine("烤鸡翅!");
}
}
效果图
命令模式的优点
1.类间解耦。调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需要调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行
2.可扩展性。Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合
3.命令模式结合其他模式会更优秀。命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少Command子类的膨胀问题。
命令模式的缺点
Command子类中如果有N个命令,问题就出来了,Comma的子类就不可以。