命令模式
将请求封装成对象,让你可以将客户端的不同请求参数化,并配合队列、记录、复原等方法来执行请求操作。
命令模式的定义
参与者说明如下:
- Command(命令界面):定义命令封装后要具备的操作界面。
- ConcreteCommand(命令实现):实现命令封装和界面,会包含每一个命令的参数和Receiver(功能执行者)。
- Receiver(功能执行者):被封装在ConcreteCommand(命令实现)类中,真正执行功能的类对象
- Client(客户端/命令发起者):产生命令的客户端,可以视情况设置命令给Receiver(功能执行者)
- Invoker(命令管理者):命令对象的管理容器或是管理类,并负责要求每个Command(命令)执行其功能。
具体实现
// 執行命令的介面
public abstract class Command
{
public abstract void Execute();
}
// 將命令和Receiver1物件繫結起來
public class ConcreteCommand1 : Command
{
Receiver1 m_Receiver = null;
string m_Command = "";
public ConcreteCommand1(Receiver1 Receiver, string Command)
{
m_Receiver = Receiver;
m_Command = Command;
}
public override void Execute()
{
m_Receiver.Action(m_Command);
}
}
// 將命令和Receiver2物件繫結起來
public class ConcreteCommand2 : Command
{
Receiver2 m_Receiver = null;
int m_Param = 0;
public ConcreteCommand2(Receiver2 Receiver, int Param)
{
m_Receiver = Receiver;
m_Param = Param;
}
public override void Execute()
{
m_Receiver.Action(m_Param);
}
}
// 負責執行命令1
public class Receiver1
{
public Receiver1()
{
}
public void Action(string Command)
{
Debug.Log("Receiver1.Action:Command[" + Command + "]");
}
}
// 負責執行命令2
public class Receiver2
{
public Receiver2()
{
}
public void Action(int Param)
{
Debug.Log("Receiver2.Action:Param[" + Param.ToString() + "]");
}
}
// 命令管理者
public class Invoker
{
List<Command> m_Commands = new List<Command>();
// 加入命令
public void AddCommand(Command theCommand)
{
m_Commands.Add(theCommand);
}
// 執行命令
public void ExecuteCommand()
{
// 執行
foreach (Command theCommand in m_Commands)
theCommand.Execute();
// 清空
m_Commands.Clear();
}
}
命令模式的使用
从上述范例看似颇为简单,正因为如此,让命令模式(Command)在实现上的弹性非常大,也出现许多变化的形式。在实际分析时,可以着重在“命令对象”的“操作行为”加以分析:
- 如果希望让“命令对象”能包含最多可能的执行方法数量,那么就加强在命令群组的设计分析。以餐厅点餐的例子来看,就是要思考,是否将点餐与饮料的点餐单合并为一张。
- 如果希望让命令可以任意执行和撤销,那么就需要着重在命令管理者(Invoker)的设计实现上。以餐厅点餐的例子来看,就是要思考这些点餐单是要人工管理还是要使用计算机系统来辅助管理
- 此外,如果让命令具备任意撤销或不执行的功能,那么系统对于命令的“反向操作”的定义也必须加以实现,或者将方向操作的执行参数,也一并封装在命令类中。
需要实现大量请求的命令
一个中小型规模的多人在线游戏,Server与Client之间的请求命令可能多达上千个,若每一个请求命令都需产生类,会发生“类过多”的问题。为了避免这样的问题发生,可以改用下列的方式来实现:
- 使用“注册回调函数(Callback Function)”:同样将所有的命令以管理容器组织起来,并正对每一个命令,注册一个回调函数(Callback Function),并将“功能执行者”(Receiver)改为一个“函数/方法”,而非类对象。最后,将多个相同功能的回调函数(Callback Function)以一个类封装在一起。
- 使用泛型程序设计:将命令界面(ICommand)以泛型方式来设计,将“功能执行者”(Receiver)定义为泛型类,命令执行时调用泛型类中的“固定方法”。但以这种方式实现时,限制会比较大,必须限定每个命令可以封装的参数数量,而且封装参数的名称比较不直观,也就是将参数以Parm1、Param2的方式命名。