命令模式(基本了解的程度)
第二次复习
1.感觉命令模式最大的特点是 包含一个执行者。
所以如果是做撤销动作,组合动作,那么就会容易推导出命令模式。
2.另外为了解耦,命令对象一般还需要 一个invoker调用者,这样客户就不必知道命令的细节了。只需要知道invoker就可以了。
第一次学习
Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests, and support undoable operations.
命令模式总不得要领。
做了3次迭代。
个人直觉,这个模式不会太常用,完全可以用数字或字符来代替命令, 解耦和简洁之间并非每次都是解耦胜出。
做菜这个场合,我站简洁。也可能网上例子都是做菜,所以才会让人觉得命令模式没有必要。应该是例子本来就不适合标准的命令模式。要不是 GOF他们4个人设计过度。
而且,如果要真正的符合开闭原则,client 不应该可以直接接触到command, 因为command 里面包含一个 receiver。什么鬼?客户端只想发送一个指令,但是这个指令,却包含了一个执行者,完全没有解耦。(再看一次,发现是自己没有完整实现命令模式,完整的模式包含一个invoker. 来组合command和receiver。client只接触invoker.虽然错误理解了模式,但是却发现了正确模式,也算理解开闭原则。)
command 模式还必须再加一层。让client 只接触指令。而这个指令不能包含Reiceiver。有空在实践一次。
v1:简单的执行方法:waiter.BaoziCommand()
public class Command { //Encapsulate a request as an object,thereby letting you parameterize clients with different //requests,queue or log requests, and support undoable operations. //将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能 //命令模式很多目的,我们来实现其中一个简单的目的:记录请求。看看是否可以自行重构出命令模式。 //1.首先用点菜来作为模拟场景。需要厨师来做菜,客人不能直接调用厨师的方法,所以需要一个服务员来作为客人和服务员的中介,这个是符合正常人思维并与现实吻合的。 //第一版可以正常工作。可以看到唯一的不完美就是CommandInvoker中,创建命令的接受者没有符合开闭原则。 //但是那个是工厂模式的事情。并且我们这里假设厨师是相对稳定的,不稳定的是客户的点餐,以致我们可以从适度设计的原则来抛弃使用工厂模式的想法。 //2.实现记录命令功能,这里就是 某个常客说,来早餐,和昨天一样。 按道理很简单。存储一下啊。仔细看看BaoziCommand()。这个是方法没法储存。好吧开始迭代升级。 public void Run() { String BreakFast=""; CommandInvoker waiter=new CommandInvoker(); BreakFast+= waiter.BaoziCommand(); BreakFast+=waiter.EggCommand(); BreakFast+=waiter.ChangFenCommand(); LSComponentsHelper.LS_Log.Log_INFO(BreakFast); } //命令调用者(服务员):接受客户调用,并且最终去调用厨师的方法。 public class CommandInvoker { private ICommandReceiver mBaozi=new BaoziTony(); private ICommandReceiver mEgg=new EggXiaoZhang(); private ICommandReceiver mChangfen=new ChangFenLaoWang(); public String BaoziCommand() { return mBaozi.ExecuteCommand(); } public String EggCommand() { return mEgg.ExecuteCommand(); } public String ChangFenCommand() { return mChangfen.ExecuteCommand(); } } //处理命令接口(抽象厨师) public interface ICommandReceiver { public String ExecuteCommand(); } public class BaoziTony implements ICommandReceiver { @Override public String ExecuteCommand() { return "BaoZi"; } } public class EggXiaoZhang implements ICommandReceiver { @Override public String ExecuteCommand() { return "Egg"; } } public class ChangFenLaoWang implements ICommandReceiver { @Override public String ExecuteCommand() { return "ChangFen"; } } }
v2:实现了命令和执行的简单解耦。
public class CommandV2 { //实现记录命令功能,这里就是 某个常客说,来早餐,和昨天一样。 按道理很简单。存储一下啊。仔细看看BaoziCommand()。这个是方法没法储存。好吧开始迭代升级。 //很明显,发现我们使用最简迭代,达到了我们的目标,但是却不是标准的命令模式.先看看我们是否符合开闭原则。如果我们符合,那么肯定是写模式设计这本书的作者过度设计。 //对于点餐来说,我们完全符合开闭原则。yesterdayList.add(EggCode);可以随便组合。非常灵活.那么可以肯定,一定是命令模式过度设计了。 //ok,那我们也过度设计一下。假设菜品变动比较大。(基本不可能,所以命令模式在这里,确实是设计过度) //假设菜品变动比较大。yesterDayAgain,这个方法违背了开闭原则。 private final Integer baoziCode=1; private final Integer EggCode=2; private final Integer ChangFenCode=3; public void Run() { String BreakFast=""; CommandInvoker waiter=new CommandInvoker(); BreakFast+= waiter.BaoziCommand(); BreakFast+=waiter.EggCommand(); BreakFast+=waiter.ChangFenCommand(); LSComponentsHelper.LS_Log.Log_INFO(BreakFast); //把小胖崽的早餐记录下。 List<Integer> yesterdayList=new ArrayList<>(); yesterdayList.add(baoziCode); yesterdayList.add(EggCode); yesterdayList.add(ChangFenCode); waiter.mOrderHistory.put("fatBoy",yesterdayList ); //第二天直接告诉老板:老板,早餐,照旧。 LSComponentsHelper.LS_Log.Log_INFO(waiter.yesterDayAgain("fatBoy")); } //命令调用者(服务员):接受客户调用,并且最终去调用厨师的方法。 public class CommandInvoker { private ICommandReceiver mBaozi=new BaoziTony(); private ICommandReceiver mEgg=new EggXiaoZhang(); private ICommandReceiver mChangfen=new ChangFenLaoWang(); public Map<String,List<Integer>> mOrderHistory=new HashMap<String,List<Integer>>(); public String BaoziCommand() { return mBaozi.ExecuteCommand(); } public String EggCommand() { return mEgg.ExecuteCommand(); } public String ChangFenCommand() { return mChangfen.ExecuteCommand(); } public String yesterDayAgain(String guest) { String res=""; List<Integer> commands=mOrderHistory.get(guest); for(Integer command:commands) { if(command==baoziCode) { res+=(BaoziCommand()); } else if(command==EggCode) { res+=(EggCommand()); } else if(command==ChangFenCode) { res+=(ChangFenCommand()); } } return res; } } //处理命令接口(抽象厨师) public interface ICommandReceiver { public String ExecuteCommand(); } public class BaoziTony implements ICommandReceiver { @Override public String ExecuteCommand() { return "BaoZiv2"; } } public class EggXiaoZhang implements ICommandReceiver { @Override public String ExecuteCommand() { return "Eggv2"; } } public class ChangFenLaoWang implements ICommandReceiver { @Override public String ExecuteCommand() { return "ChangFenv2"; } } }
v3,把命令封装成类。更面向对象,变成了标准的命令模式。 但是个人感觉没有 v2简洁。
public class CommandV3 { //假设菜品变动比较大。yesterDayAgain,这个方法违背了开闭原则。 //我们应该可以让baoziCode,每个菜名,都会关联一个厨师的方法。这样通过属性和方法的结合,也就是类,达到开闭原则。 //小结一下,对于常见的方法调用XXX.fun1().XXX.fun2(). 无法保存和组合方法顺序。所以最简方案。我们会用数字或字符1,2,3来代替方法xxx.fun1,fun2,fun3 //但是稍微面向对象一点,我们可以把方法放入到某个类中,那么意思就是把方法的执行者也包进来。就是这么简单。 //再回顾定义:Encapsulate a request as an object,没毛病,把方法放到类中,所以一并把方法的执行者也放到类中。完毕。 //至于标准的uml图。我觉得invoker在这里是多余,命令类已经包含执行者。不需要invoker. //waiter.CheckAndExecuteCommand(BaoziCode) 和BaoziCode.OneReceiverWillDoIt 那个更简洁? //个人直觉,这个模式不会太常用,完全可以用数字或字符来代替命令, 解耦和简洁之间并非每次都是解耦胜出。这个场合,我站简洁。 public void Run() { String BreakFast=""; CommandInvoker waiter=new CommandInvoker(); AbsCommand BaoziCode=new BaoziCommand(); BreakFast+= waiter.CheckAndExecuteCommand(BaoziCode); AbsCommand EggCode=new EggCommand(); BreakFast+= waiter.CheckAndExecuteCommand(EggCode); AbsCommand ChangFenCode=new ChangFenCommand(); BreakFast+= waiter.CheckAndExecuteCommand(ChangFenCode); AbsCommand jianJiaoCode=new JianJiaoCommand(); BreakFast+= waiter.CheckAndExecuteCommand(jianJiaoCode); LSComponentsHelper.LS_Log.Log_INFO(BreakFast); //把小胖崽的早餐记录下。 List<AbsCommand> yesterdayList=new ArrayList<>(); yesterdayList.add(BaoziCode); yesterdayList.add(EggCode); yesterdayList.add(ChangFenCode); yesterdayList.add(jianJiaoCode); waiter.mOrderHistory.put("fatBoy",yesterdayList ); //第二天直接告诉老板:老板,早餐,照旧。 LSComponentsHelper.LS_Log.Log_INFO(waiter.yesterDayAgain("fatBoy")); } //region command //抽象命令(抽象菜名) public interface AbsCommand { public String OneReceiverWillDoIt(); } //命令的实现者(菜名和执行厨师类) public class BaoziCommand implements AbsCommand { private ICommandReceiver mCommandReceiver=new BaoziTony(); @Override public String OneReceiverWillDoIt() { return mCommandReceiver.ExecuteCommand(); } } public class EggCommand implements AbsCommand { private ICommandReceiver mCommandReceiver=new EggXiaoZhang(); @Override public String OneReceiverWillDoIt() { return mCommandReceiver.ExecuteCommand(); } } public class ChangFenCommand implements AbsCommand { private ICommandReceiver mCommandReceiver=new ChangFenLaoWang(); @Override public String OneReceiverWillDoIt() { return mCommandReceiver.ExecuteCommand(); } } //新加一个菜品:煎饺 public class JianJiaoCommand implements AbsCommand { private ICommandReceiver mCommandReceiver=new JiaoJiaoLiu(); @Override public String OneReceiverWillDoIt() { return mCommandReceiver.ExecuteCommand(); } } //endregion //region Invoker //命令调用者(服务员):接受客户调用,并且最终去调用厨师的方法。 public class CommandInvoker { public Map<String,List<AbsCommand>> mOrderHistory=new HashMap<String,List<AbsCommand>>(); //还是需要Invoker来执行,因为可能需要更换command的Receiver(需要服务员来做决定,是否要更换厨师做某个菜。) public String CheckAndExecuteCommand(AbsCommand command) { return command.OneReceiverWillDoIt(); } public String yesterDayAgain(String guest) { String res=""; List<AbsCommand> commands=mOrderHistory.get(guest); for(AbsCommand command:commands) { res+=command.OneReceiverWillDoIt(); } return res; } } //endregion //region Receiver //处理命令接口(抽象厨师) public interface ICommandReceiver { public String ExecuteCommand(); } public class BaoziTony implements ICommandReceiver { @Override public String ExecuteCommand() { return "BaoZiv2"; } } public class EggXiaoZhang implements ICommandReceiver { @Override public String ExecuteCommand() { return "Eggv2"; } } public class ChangFenLaoWang implements ICommandReceiver { @Override public String ExecuteCommand() { return "ChangFenv2"; } } public class JiaoJiaoLiu implements ICommandReceiver { @Override public String ExecuteCommand() { return "jianjiaov2"; } } //endregion }