需要做多个命令的组合、命令队列(包括对队列的操作)、增加命令日志、撤销重做 这些操作的时候就提现出Invoker的作用了
命令模式关注的是对命令本身的处理
命令模式:将一个请求封装为一个对象,从而可用不同的的请求对客户进行参数化,队请求排队或者记录请求日志,以及支持可撤销的操作。
1. 模式中角色
1.1 抽象命令(Command):定义命令的接口,声明执行的方法。
1.2 具体命令(ConcreteCommand):具体命令,实现要执行的方法,它通常是“虚”的实现;通常会有接收者,并调用接收者的功能来完成命令要执行的操作。
1.3 接收者(Receiver):真正执行命令的对象。任何类都可能成为一个接收者,只要能实现命令要求实现的相应功能。
1.4 调用者(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
1.5 客户端(Client):命令由客户端来创建,并设置命令的接收者。
2. 模式解读
2.1 命令模式的类图
3.命令模式实例
①接口ICommand:用来声明抽象一个命令操作的接口。
②ConcreteCommand:具体的命令实现,用来表示命令的类型例如上边场景中的开发项目和维护项目可分别实现为:开发项目命令,维护项目命令 等。
③Receiver:具体命令的实现者,也就是说命令的执行者,相对场景中的开发者。
④Invoker:命令的指挥者,用来设置命令,并且通知执行命令,相对于上边场景中的项目经理。
了解完命令模式的概念后,我们用命令模式来实现上边的场景:
首先是命令接口ICommand的实现,代码如下:
{
void Excute(WebProject project);
}
接下来就是我们接受命令并且执行的实现者啦,也就是我们场景中的开发者,代码如下:
/// 开发者--Receive
/// </summary>
public class Developer
{
/// <summary>
/// 开发项目
/// </summary>
/// <param name="project"> 项目 </param>
public void Coding(WebProject project)
{
Console.WriteLine( " 开发{0}项目 " , project.ProjectName);
}
/// <summary>
/// 运维项目
/// </summary>
/// <param name="project"> 项目 </param>
public void UpdateRoutine(WebProject project)
{
Console.WriteLine( " 维护{0}项目 " , project.ProjectName);
}
}
现在有了命令的实现者,就应该有具体的命令啦,上边那场景中的命令分别有,开发命令与维护命令,具体代码如下:
/// 开发项目命令--实际调用的都是开发者的工作
/// </summary>
public class CodingCommand : ICommand
{
Developer dp { get; set; }
public WebProject project { get; set; } // 所对应的项目
public void Excute()
{
dp.Coding(project);
}
}
/// <summary>
/// 维护项目命令--实际调用的都是开发者的工作
/// </summary>
public class UpdateRoutineCommand : ICommand
{
Developer dp { get; set; }
public WebProject project { get; set; } // 所对应的项目
public void Excute()
{
dp.UpdateRoutine(project);
}
}
命令与实现者都就绪了,指挥调度者就该出现了,也就是项目经理,项目经理用来负责设置命令调度命令执行,代码如下:
/// 项目经理类--Invoker
/// </summary>
public class ProjectManager
{
List<ICommand> cmdList = new List<ICommand>();
/// <summary>
/// 设置命令
/// </summary>
/// <param name="cmd"></param>
public void SetCommand(ICommand cmd)
{
cmdList.Add(cmd);
}
/// <summary>
/// 发起命令---以一个队列来控制
/// </summary>
public void ExcuteCommand()
{
cmdList.ForEach(p => p.Excute());
}
}
代码写好后,我们来模拟一下调用吧,现在项目经理需要分配给开发者两个任务,一个是开发项目A,另一个是维护项目B!主函数调用如下:
WebProject wpA = new WebProject() { ProjectName = " 项目A " };
// 表示一个具体的项目名字叫:项目B
WebProject wpB = new WebProject() { ProjectName = " 项目B " };
// 开发者
Developer developer= new Developer();
// 开发者所需要接收的命令
ICommand codeCmd = new CodingCommand() { project = wpA, dp = developer };
ICommand UpdateCmd = new UpdateRoutineCommand() { project = wpB, dp = developer };
// 项目经理
ProjectManager pm = new ProjectManager();
// 设置命令
pm.SetCommand(codeCmd);
pm.SetCommand(UpdateCmd);
// 发起命令让开发者去完成
pm.ExcuteCommand();
}
那么一个简单的命令模式就完成了。
3.更加灵活的分配任务
接下来我们在增加一些要求:
①项目经理每次发起的命令都需要记录下来,年底好根据开发者的工作量评估奖金。
②项目经理可以撤销发起的命令,例如:撤销维护项目B的命令,让开发者专心做项目A的开发工作。
回顾下命令模式的定义,上边两条需求就等同于定义中的“队请求排队或者记录请求日志,以及支持可撤销的操作”
那么我们给需求中加入一个方法用来撤销命令,因为项目经理类中已经有一个集合来记录命令的总数了,所以已经实现了请求记录的功能啦,我们只需加入撤销功能就好了。
首先,给ICommand接口加入一个Cancel方法,用来表示撤销操作,因为命令撤销了需要通知实现者做一些撤销的工作,代码如下:
{
void Excute();
void Cancel();
}
接下来既然有取消了,那么开发者受到取消命令后要执行一些关于取消的代码,所以开发者类加入一个方法StopCoding,表示停止项目的工作,代码如下:
/// 开发者
/// </summary>
public class Developer
{
/// <summary>
/// 开发项目
/// </summary>
/// <param name="project"> 项目 </param>
public void Coding(WebProject project)
{
Console.WriteLine( " 开发{0}项目 ", project.ProjectName);
}
/// <summary>
/// 运维项目
/// </summary>
/// <param name="project"> 项目 </param>
public void UpdateRoutine(WebProject project)
{
Console.WriteLine( " 维护{0}项目 ", project.ProjectName);
}
/// <summary>
/// 停止项目的工作
/// </summary>
/// <param name="project"></param>
public void StopCoding(WebProject project)
{
Console.WriteLine( " 停止{0}项目 ",project.ProjectName);
}
}
修改实际的命令类实现撤销命令的方法,并调用开发者类的StopCoding方法做撤销命令的工作,代码如下:
/// 开发项目命令
/// </summary>
public class CodingCommand : ICommand
{
public Developer dp { get; set; }
public WebProject project { get; set; } // 所对应的项目
public void Excute()
{
dp.Coding(project);
}
/// <summary>
/// 撤销操作
/// </summary>
public void Cancel()
{
dp.StopCoding(project);
}
}
/// <summary>
/// 维护项目命令
/// </summary>
public class UpdateRoutineCommand : ICommand
{
public Developer dp { get; set; }
public WebProject project { get; set; } // 所对应的项目
public void Excute()
{
dp.UpdateRoutine(project);
}
/// <summary>
/// 撤销操作
/// </summary>
public void Cancel()
{
dp.StopCoding(project);
}
}
接下来就是命令的指挥者啦,项目经理首先设置一个撤销命令,并且将执撤销命令,在执行撤销命令的时候首先要将命令从集合中移除,因为该开发者已经不参与该撤销项目的工作了,所以不记录考核依据中,在调用命令中的撤销方法让开发者做具体的撤销工作。代码如下:
/// 项目经理类
/// </summary>
public class ProjectManager
{
List<ICommand> cmdList = new List<ICommand>();
/// <summary>
/// 设置命令
/// </summary>
/// <param name="cmd"></param>
public void SetCommand(ICommand cmd)
{
cmdList.Add(cmd);
}
/// <summary>
/// 发起命令
/// </summary>
public void ExcuteCommand()
{
cmdList.ForEach(p => p.Excute());
}
/// <summary>
/// 返回记录行数
/// </summary>
/// <returns></returns>
public int GetCommandCount()
{
return cmdList.Count;
}
/// <summary>
/// 撤销命令
/// </summary>
/// <param name="cmd"></param>
public void CancelExectueCommand(ICommand cmd)
{
cmd.Cancel();
cmdList.Remove(cmd);
}
}
可见对功能的封装和“请求与实现分离”,可以让我们很容易的进行撤销与记录的工作,可以再命令执行前作一些必须要的操作。主函数调用如下:
{
// 表示一个具体的项目名字叫:项目A
WebProject wpA = new WebProject() { ProjectName = " 项目A " };
// 表示一个具体的项目名字叫:项目B
WebProject wpB = new WebProject() { ProjectName = " 项目B " };
// 开发者
Developer developer= new Developer();
// 开发者所需要接收的命令
ICommand codeCmd = new CodingCommand() { project = wpA, dp = developer };
ICommand UpdateCmd = new UpdateRoutineCommand() { project = wpB, dp = developer };
// 项目经理
ProjectManager pm = new ProjectManager();
// 设置命令
pm.SetCommand(codeCmd);
pm.SetCommand(UpdateCmd);
// 发起命令让开发者去完成
pm.ExcuteCommand();
pm.CancelExectueCommand(UpdateCmd); // 撤销UpdateCmd命令
命令模式总结:
①它能很容易的维护所有命令的集合。
②它可以很方便的实现撤销和恢复命令。
③可以很方便的将每个执行记录日志。
④最重要的就是将发起者与实现者分离。
文章转载自 http://www.cnblogs.com/doubleliang/archive/2012/04/14/2447815.html 和 http://www.cnblogs.com/wangjq/archive/2012/07/11/2585930.html