设计模式深入学习--Command 命令模式(行为型模式)

   今天我们来介绍下 Command 命令模式。Command 命令模式是一种我们在日常游戏开发中常见的一种设计模式,比如在策略类游戏中我们会看到大量的类似设计,比如 我们操作小人 请求做一件事,然后小人去响应,这种“行为请求者”与“行为实现者”是紧耦合的。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这些情况下,将“行为请求者”与“行为实现者”解耦,实现二者之间的松耦合就至关重要。命令模式是解决这类问题的一个比较好的方法。 我们先来看看命令模式的基础代码:

/// <summary>
/// 命令类,用来声明执行操作的接口
/// </summary>
abstract class Command
{
    protected Receiver receiver;
 
    public Command(Receiver receiver)
    {
        this.receiver = receiver;
    }
 
    public abstract void Execute();
}
 
/// <summary>
/// 实体命令类,将一个接收者对象绑定一个动作,调用接收者相应的操作来实现Execute
/// </summary>
class ConcreteCommand : Command
{
    public ConcreteCommand(Receiver receiver) : base(receiver) { }
 
    public override void Execute()
    {
        receiver.Action();
    }
}
 
/// <summary>
/// Invoker类,要求该命令执行这个请求
/// </summary>
class Invoker
{
    private Command command;
 
    public void SetCommand(Command command)
    {
        this.command = command;
    }
 
    public void ExecuteCommand()
    {
        command.Execute();
    }
 
}
 
/// <summary>
/// Receiver类 知道如何实施执行一个与请求相关的操作,任何类都可能作为一个接收者
/// </summary>
class Receiver
{
    public void Action()
    {
        Console.WriteLine("执行请求");
    }
}
首先是声明一个消息的接收类Receiver ,然后声明一个虚拟的命令类Command,该命令类包含了一个接收类Receiver。然后在声明一个ConcreteCommand 实体的命令类,继承自命令类Command,并且有一个具体执行 接收类的方法。好,命令类和消息类都有了,接下来我们再来一个发消息的类 ,专门用来处理调用命令的逻辑判断类Invoker。里面保存一个命令类Command ,并且可以去执行命令类Command的接收类Receiver方法。然后运行一下。
Receiver r = new Receiver();
         Command c = new ConcreteCommand(r);
 
         Invoker i = new Invoker();
 
         i.SetCommand(c);
 
         i.ExecuteCommand();


   我们在运行代码里面看到,先声明一个Receiver类(消息接收类,也就是有具体执行代码的类),然后声明一个ConcreteCommand实体命令类,把消息放进去,在声明一个执行类Invoker,把ConcreteCommand放进去,最后执行Invoker方法。这就是命令模式的基础用法, 让实体命令类ConcreteCommand去处理命令下层逻辑, 让执行类Invoker来处理上层逻辑,判断去使用哪个命令,就好比我们鼠标点选一个小人,去攻击一个敌人,我们鼠标的操作就是执行类Invoker做的处理,而小人接收到这个命令后,再去移动攻击,而这些逻辑就是具体命令类的处理,而移动和攻击的代码 就是Receiver类的具体方法。然后现在,我们再来自己写个小例子试试命令模式的用法。

//虚拟命令类
   public abstract class BaseCommand
   {
       protected AIBehavior AIReceiver;
 
       public BaseCommand(AIBehavior AIReceiver)
       {
           this.AIReceiver = AIReceiver;
       }
       //执行命令
       public abstract void ExcuteCommand();
   }
 
   class AtkCommand : BaseCommand
   {
       public AtkCommand(AIBehavior AIReceiver) : base(AIReceiver)
       {
 
       }
 
       public override void ExcuteCommand()
       {
           AIReceiver.AttackBehavior();
       }
   }
 
   class MoveCommand : BaseCommand
   {
       public MoveCommand(AIBehavior AIReceiver) : base(AIReceiver)
       {
       }
 
       public override void ExcuteCommand()
       {
           AIReceiver.MoveBehavior();
       }
   }
 
   class FlyCommand : BaseCommand
   {
       public FlyCommand(AIBehavior AIReceiver) : base(AIReceiver)
       {
       }
 
       public override void ExcuteCommand()
       {
           AIReceiver.MoveBehavior();
       }
   }
 
 
   public class AIBehavior
   {
       public void AttackBehavior()
       {
           Console.WriteLine("攻击");
       }
 
       public void MoveBehavior()
       {
           Console.WriteLine("移动");
       }
 
       public void FlyBehavior()
       {
           Console.WriteLine("飞行");
       }
   }
 
   //执行官执行
   public class Commander
   {
       private List<BaseCommand> orders = new List<BaseCommand>();
 
       //设置命令判断逻辑
       public void SetOder(BaseCommand commands)
       {
 
           if (commands.ToString() == "命令模式.AtkCommand")
           {
               Console.WriteLine("开始设置攻击命令");
               orders.Add(commands);
           }
           else if (commands.ToString() == "命令模式.MoveCommand")
           {
               Console.WriteLine("开始设置移动命令");
               orders.Add(commands);
           }
           else
           {
               Console.WriteLine("相关命令还没有开发");
           }
       }
 
       public void CancelOrder(BaseCommand commands)
       {
           orders.Remove(commands);
           Console.WriteLine("取消命令:" + commands.ToString());
       }
 
       //执行
       public void Notify()
       {
           foreach (BaseCommand bc in orders)
           {
               bc.ExcuteCommand();
           }
       }
   }

     首先声明一个类 AIBehavior,该类有3个具体的执行方法,攻击,移动,飞行。代表一个AI所拥有的技能和具体实现。然后声明一个虚拟命令类BaseCommand, 该类包含了一个AIBehavior类,和一个虚拟的执行方法。 再来三个具体命令类,对应AIBehavior类的三个方法 ,分别是攻击命令类,移动命令类和飞行命令类,他们都分别继承命令类,然后各自重写命令类的执行方法ExcuteCommand()来调用各自对应的 攻击,移动和飞行方法。好,命令类和消息的接收类都有了,我们再来一个执行类Commander。Commander类里面有一个命名列表,和逻辑判断类,判断外部传入的类是哪个名称,然后把命令加入命令列表,当然也有一个删除命令的方法CancelOrder,还有一个Notify执行命令的方法。这样我们的执行类就可以 增删改查命令,也可以做判断处理逻辑的运算,现在来看看运行代码:

//AI行为
        AIBehavior ai = new AIBehavior();
 
        //各种命令
        BaseCommand AtkCommand = new AtkCommand(ai);
        BaseCommand MovCommand = new MoveCommand(ai);
        BaseCommand FlyCommand = new FlyCommand(ai);
 
        //命令执行官
        Commander commander = new Commander();
 
        //设置命令
        commander.SetOder(AtkCommand);
        commander.SetOder(MovCommand);
        commander.SetOder(FlyCommand);
 
        //开始执行
        commander.Notify();
 
        Console.Read();
   注释已经写的很详细了,大家一眼就看得明白了 ,下面是运行结果。

 
   以上就是我们今天要说的命令模式,我们最后来总结下命令模式,在下面的情况下可以考虑使用命令模式:
系统需要支持命令的撤销(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo方法吧命令所产生的效果撤销掉。命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果。系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。意思为:原来请求的发出者可能已经不存在了,而命令对象本身可能仍是活动的。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新。系统需要使用命令模式作为“CallBack(回调)”在面向对象系统中的替代。Callback即是先将一个方法注册上,然后再以后调用该方法。
  命令模式使得命令发出的一个和接收的一方实现低耦合,从而有以下的优点:
  命令模式使得新的命令很容易被加入到系统里。
  可以设计一个命令队列来实现对请求的Undo和Redo操作。
  可以较容易地将命令写入日志。
  可以把命令对象聚合在一起,合成为合成命令。合成命令式合成模式的应用。
  命令模式的缺点:
    使用命令模式可能会导致系统有过多的具体命令类。这会使得命令模式在这样的系统里变得不实际。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值