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

浙公网安备 33010602011771号