命令模式(基本了解的程度)

第二次复习
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
}
原文地址:https://www.cnblogs.com/lsfv/p/11141884.html