无废话C#设计模式之十八:Command
意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
场景
我们知道,网络游戏中的客户端需要不断把当前人物的信息发送到游戏服务端进行处理(计算合法性、保存状态到数据库等)。假设有这样一种需求,在服务端收到客户端的请求之后需要判断两次请求间隔是不是过短,如果过短的话就考虑可能是游戏外挂,不但不执行当前请求还要把前一次请求进行回滚。暂且把问题简单化一点不考虑客户端和服务端之间的通讯来进行程序设计的话,你可能会创建一个Man类型,其中提供了一些人物移动的方法,执行这些方法后,服务端内存中的人物会进行一些坐标的修改。客户端定时调用Man类型中的这些方法即可。那么如何实现防外挂的需求呢?你可能会想到在Man方法中保存一个列表,每次客户端调用方法的时候把方法名和方法调用的时间保存进去,然后在每个方法执行之前就进行判断。这样做有几个问题:
l Man类型应该只是负责执行这些操作的,是否应该执行操作的判断放在Man类型中是否合适?
l 如果方法的调用还有参数的话,是不是需要把方法名、方法的参数以及方法调用时间都保存到列表中呢?
l 如果需要根据不同的情况回滚一组行为,比如把Man类型的方法分为人物移动和装备损耗,如果客户端发送命令的频率过快希望回滚所有人物移动的行为,如果客户端发送命令的频率过慢希望回滚所有装备损耗的行为。遇到这样的需求怎么实现呢?
由此引入命令模式,命令模式的主要思想就是把方法提升到类型的层次,这样对方法的执行有更多的控制力,这个控制力表现在对时间的控制力、对撤销的控制力以及对组合行为的控制力。
示例代码
using System; using System.Collections.Generic; using System.Text;
namespace CommandExample { class Program { static void Main(string[] args) { Man man = new Man(); Server server = new Server(); server.Execute(new MoveForward(man, 10)); System.Threading.Thread.Sleep(50); server.Execute(new MoveRight(man, 10)); server.Execute(new MoveBackward(man, 10)); server.Execute(new MoveLeft(man, 10)); } }
class Man { private int x = 0; private int y = 0;
public void MoveLeft(int i) { x -= i; }
public void MoveRight(int i) { x += i; }
public void MoveForward(int i) { y += i; }
public void MoveBackward(int i) { y -= i; }
public void GetLocation() { Console.WriteLine(string.Format("({0},{1})", x, y)); } }
abstract class GameCommand { private DateTime time;
public DateTime Time { get { return time; } set { time = value; } }
protected Man man;
public Man Man { get { return man; } set { man = value; } }
public GameCommand(Man man) { this.time = DateTime.Now; this.man = man; }
public abstract void Execute();
public abstract void UnExecute(); }
class MoveLeft : GameCommand { int step;
public MoveLeft(Man man, int i) : base(man) { this.step = i; }
public override void Execute() { man.MoveLeft(step); }
public override void UnExecute() { man.MoveRight(step); } }
class MoveRight : GameCommand { int step;
public MoveRight(Man man, int i) : base(man) { this.step = i; }
public override void Execute() { man.MoveRight(step); }
public override void UnExecute() { man.MoveLeft(step); } }
class MoveForward : GameCommand { int step;
public MoveForward(Man man, int i) : base(man) { this.step = i; }
public override void Execute() { man.MoveForward(step); }
public override void UnExecute() { man.MoveBackward(step); } }
class MoveBackward : GameCommand { int step;
public MoveBackward(Man man, int i) : base(man) { this.step = i; }
public override void Execute() { man.MoveBackward(step); }
public override void UnExecute() { man.MoveForward(step); } }
class Server { GameCommand lastCommand;
public void Execute(GameCommand cmd) { Console.WriteLine(cmd.GetType().Name); if (lastCommand !=null && (TimeSpan)(cmd.Time - lastCommand.Time) < |