设计模式系列-命令模式新的一年的春天到啦,又是一轮跳槽离职的高峰期,面对新的一年的开始大家都希望各个方
设计模式系列-命令模式
新的一年的春天到啦,又是一轮跳槽离职的高峰期,面对新的一年的开始大家都希望各个方面都在更上一层楼,难免会出现跳槽、离职创业等等现象,最近项目组有人离职啦,当然人走了活还是要有人干的嘛,如何合理的利用有限的人力资源把多余的活分配出去呢?这就是项目经理们考虑的事情啦!不过一般还是会出现一个开发人员同时兼有几个项目的现象啦~!OK,这个就是今天的场景,我们来用代码模拟一下吧!
????????1.场景模拟
????????首先我们考虑,当项目多,人手不够时,我们需要一个人去同时兼有好几个项目的工作,可能有开发也可能有运维!
??????? 那么第一步我们先设计一个项目类,分别代表需要参与的项目,代码如下:
??? ///?<summary>
????///?Web项目类
????///?</summary>
????public?class?WebProject
????{
????????///?<summary>
????????///?项目名称
????????///?</summary>
????????public?string?ProjectName?{?get;?set;?}
????}
???????? 接下来第二步,我们设计一个开发者类表示具体的开发人员,并且开发人员具有开发项目和运维项目的操作,代码如下:
????///?<summary>
????///?开发者
????///?</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);
????????}
????}
????????主函数调用方法如下:
??? static?void?Main(string[]?args)
??? {
????????????//表示一个具体的项目名字叫:项目A
????????????WebProject?wpA?=?new?WebProject()?{?ProjectName?=?"项目A"?};
????????????//表示一个具体的项目名字叫:项目B
????????????WebProject?wpB?=?new?WebProject()?{?ProjectName?=?"项目B"?};
????????????//实例化一个开发者,程序员
????????????Developer?dp?=?new?Developer();
????????????//命令他开发项目A
????????????dp.Coding(wpA);
????????????//命令他开发项目B
????????????dp.UpdateRoutine(wpB);
??? }
??????? 这么简单的程序运行结果我就不贴出来啦,我们来看看上边的代码,我们把主函数的调用当做客户,我们发现如果客户直接和开发者形成这种紧耦合的状态是非常不好的,比如说,客户会提出很多业务上的要求,而开发者并不能很准确的估算出开发成本与时间,所以我们需要一个项目经理来与客户沟通,并且将客户提出的不合理需求过滤掉,在给开发人员发出一个命令将项目交给开发人员!也就是说需要将“命令发起者”与“命令实现者”分离!
????????2.命令模式
?????????那么我们来看看实现“命令发起者”与“命令实现者”分离的设计模式,命令模式:
?????????命令模式:将一个请求封装为一个对象,从而可用不同的的请求对客户进行参数化,队请求排队或者记录请求日志,以及支持可撤销的操作。
???????? 命令模式结构图如下:
??????
?????? ①接口ICommand:用来声明抽象一个命令操作的接口。
?????? ②ConcreteCommand:具体的命令实现,用来表示命令的类型例如上边场景中的开发项目和维护项目可分别实现为:开发项目命令,维护项目命令等。
?????? ③Receiver:具体命令的实现者,也就是说命令的执行者,相对于上边场景中的开发者。
???????④Invoker:命令的指挥者,用来设置命令,并且通知执行命令,相对于上边场景中的项目经理。
?????? 了解完命令模式的概念后,我们用命令模式来实现上边的场景:
??????? 首先是命令接口ICommand的实现,代码如下:
????public?interface?ICommand
????{
????????void?Excute(WebProject project);
????}
??????? 接下来就是我们接受命令并且执行的实现者啦,也就是我们场景中的开发者,代码如下:
????///?<summary>
????///?开发者
????///?</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>
????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);
????????}
????}
?????? 命令与实现者都就绪了,指挥调度者就该出现了,也就是项目经理,项目经理用来负责设置命令调度命令执行,代码如下:
????///?<summary>
????///?项目经理类
????///?</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!主函数调用如下:
????????????//表示一个具体的项目名字叫:项目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();
}
?????? 那么一个简单的命令模式就完成了。
????? 3.更加灵活的分配任务
?????? 接下来我们在增加一些要求:
?????? ①项目经理每次发起的命令都需要记录下来,年底好根据开发者的工作量评估奖金。
???????②项目经理可以撤销发起的命令,例如:撤销维护项目B的命令,让开发者专心做项目A的开发工作。
?????? 回顾下命令模式的定义,上边两条需求就等同于定义中的“队请求排队或者记录请求日志,以及支持可撤销的操作”
?????? 那么我们给需求中加入一个方法用来撤销命令,因为项目经理类中已经有一个集合来记录命令的总数了,所以已经实现了请求记录的功能啦,我们只需加入撤销功能就好了。
???????首先,给ICommand接口加入一个Cancel方法,用来表示撤销操作,因为命令撤销了需要通知实现者做一些撤销的工作,代码如下:
????public?interface?ICommand
????{
????????void?Excute();
????????void?Cancel();
????}
????? 接下来既然有取消了,那么开发者受到取消命令后要执行一些关于取消的代码,所以开发者类加入一个方法StopCoding,表示停止项目的工作,代码如下:
??///?<summary>
????///?开发者
????///?</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>
????///?开发项目命令
????///?</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>
????///?项目经理类
????///?</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);
????????}
????}
???? 可见对功能的封装和“请求与实现分离”,可以让我们很容易的进行撤销与记录的工作,可以再命令执行前作一些必须要的操作。主函数调用如下:
??????? static?void?Main(string[]?args)
????????{
????????????//表示一个具体的项目名字叫:项目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命令?????????}
?????? 命令模式总结:
?????? ①它能很容易的维护所有命令的集合。
?????? ②它可以很方便的实现撤销和恢复命令。
?????? ③可以很方便的将每个执行记录日志。
???????④最重要的就是将发起者与实现者分离。