美文网首页程序员
学好设计模式防被祭天:命令模式

学好设计模式防被祭天:命令模式

作者: 阿菜的博客 | 来源:发表于2017-08-29 23:06 被阅读94次
    命令模式

    为了防止被“杀”了祭天,学点设计模式,并总结下还是有必要的。

    一:理解

    1. 命令模式作用于调用者和被调用者之间,新建命令对象。调用者无需直接持有被调用者对象,实现两者之间的解耦。
    2. 例如在计算机网络中,客户端向服务器发送请求/命令,客户端不持有服务器对象,而是向服务器发一个包,包中含有服务器地址,以及命令的具体内容。这和命令模式类似,抽取了命令对象。

    二:例子

    你是个富二代,家里有很多仆人。

    为了方便,这里只列出程序员(Programmer)和女仆(Maidservant)。

    当你无聊的时候,你就会找你的仆人去玩耍。

    你希望可以设计一个优雅的系统来解决这个玩耍问题,也好在二代圈子里炫耀一番。

    于是你叫来了程序员小菜帮你完成这个玩耍程序。

    小菜上来就是一顿敲,新建了程序员类和女仆类。

    程序员有两个方法,洗键盘和敲代码。

    女仆也有两个方法,洗澡和papapa。

    // 程序员类
    public class Programmer {
        public void washKeyboard() {
            System.out.println("洗键盘!");
        }
    
        public void program() {
            System.out.println("表演敲代码!");
        }
    }
    
    // 女仆类
    public class Maidservant {
        public void wash() {
            System.out.println("洗澡!");
        }
    
        public void papapa() {
            System.out.println("如果你追到我,我就和你嘿嘿嘿!");
        }
    }
    

    同时,他也为你设计了一个富二代(FuErDai)类,包括两个属性,分别是程序员和女仆,同时包含两个方法,分别是和程序员玩,和女仆玩。

    程序员比较无聊,和程序员玩就是直接让程序员洗键盘和敲代码。

    和女仆玩就比较有意思,可以先洗澡,然后再papapa。

    @Data
    public class FuErDai {
        private Programmer programmer;
        private Maidservant maidservant;
    
        public void playWithProgrammer() {
            programmer.washKeyboard();
            programmer.program();
        }
    
        public void playWithServant() {
            maidservant.wash();
            maidservant.papapa();
        }
    }
    

    同时附上一个测试代码:

    public class ClientV1 {
        public static void main(String[] args) {
            FuErDai fuErDai = new FuErDai();
            fuErDai.setProgrammer(new Programmer());
            fuErDai.setMaidservant(new Maidservant());
            fuErDai.playWithProgrammer();
            fuErDai.playWithServant();
        }
    }
    

    输入/输出:

    洗键盘!
    表演敲代码!
    洗澡!
    如果你追到我,我就和你嘿嘿嘿!

    你觉得这个代码挺有意思的,就拿到二代圈子去炫耀。

    他们真的很严格

    没想到你的朋友真的很严格,对这个程序各种挑毛病。你炫耀不成,反倒被嘲讽了一番。

    你的朋友发现,你只需要和你的仆人玩耍而已,为什么需要知道他们具体怎么玩呢,例如在和女仆玩的时候,你需要知道她有wash和papapa方法。

    如果哪天女仆不想在papapa之前洗澡了,去掉了自己的wash方法,但你并没有引用女仆提供的最新jar包,当你想和女仆玩耍时,就出错了。

    和女仆之间有太多的关系,不好不好。

    你灰头土脸地回到家,气得直拍桌子,并拿出藏在地窖深处的鹤顶红,准备杀了这位程序员祭天。

    拍桌子

    吓得小菜急忙连夜修改代码。

    他提取了一个仆人(Servant)接口,声明了playWithFuErDai方法。

    程序员和女仆同时实现这个接口,对外提供playWithFuErDai方法。

    public interface Servant {
        void playWithFuErDai();
    }
    
    // 程序员类
    public class Programmer implements Servant{
        private void washKeyboard() {
            System.out.println("洗键盘!");
        }
    
        private void program() {
            System.out.println("表演敲代码!");
        }
    
        @Override
        public void playWithFuErDai() {
            washKeyboard();
            program();
        }
    }
    
    // 女仆类
    public class Maidservant implements Servant{
        private void wash() {
            System.out.println("洗澡!");
        }
    
        private void papapa() {
            System.out.println("如果你追到我,我就和你嘿嘿嘿!");
        }
    
        @Override
        public void playWithFuErDai() {
            wash();
            papapa();
        }
    }
    

    修改之后的富二代代码,只需直接调用仆人的playWithFuErDai方法,而不用关心仆人到底是怎么玩的。

    @Data
    public class FuErDai {
        private Programmer programmer;
        private Maidservant maidservant;
    
        public void playWithProgrammer() {
            programmer.playWithFuErDai();
        }
    
        public void playWithServant() {
            maidservant.playWithFuErDai();
        }
    }
    

    你觉得这个代码还行,就原谅了小菜。

    一段时间后,你发现和程序员玩耍真的是太没意思了,不如和你的宠物狗玩耍。

    于是,你就命令小菜修改这个玩耍程序。

    小菜思考了一下:

    1. 富二代的喜好经常变化,如果只去掉富二代类的程序员属性,加一个狗属性,那下次仍旧需要修改。
    2. 富二代其实只需要有两个“按键”,按下去就可以找到对象进行玩耍,而不固定和谁玩耍。
    3. 真正应该属于富二代的属性应该是两个命令,而不是两个陪他玩耍的“人”。

    小菜决定使用命令模式

    他先新建了一个命令接口,申明了execute方法,命令就是用来被执行的。

    public interface Command {
        void execute();
    }
    

    他还新建了两个具体的命令类,分别是和仆人玩耍命令以及和狗玩耍命令。

    和仆人玩耍命令类包含了一个仆人属性,在execute方法中,直接调用仆人的playWithFuErDai方法即可。

    和狗玩耍的命令类似。

    // 和程序员玩耍命令
    @Data
    public class PlayWithServantCommand implements Command {
        private Servant servant;
    
        @Override
        public void execute() {
            servant.playWithFuErDai();
        }
    }
    
    // 和狗玩耍命令
    @Data
    public class PlayWithDogCommand implements Command {
        Dog dog;
    
        @Override
        public void execute() {
            dog.playWithFuErDai();
        }
    }
    

    同时,小菜也重构了富二代类,包含两个命令属性,以及两个用于触发命令的playOne和playTwo方法。

    @Data
    public class FuErDai {
        private Command commandOne;
        private Command commandTwo;
    
        public void playOne() {
            commandOne.execute();
        }
    
        public void playTwo() {
            commandTwo.execute();
        }
    }
    

    测试代码:

    public class ClientV2 {
        public static void main(String[] args) {
            PlayWithServantCommand playWithServantCommand = new PlayWithServantCommand();
            Maidservant maidservant = new Maidservant();
            playWithServantCommand.setServant(maidservant);
            PlayWithDogCommand playWithDogCommand = new PlayWithDogCommand();
            Dog dog = new Dog();
            playWithDogCommand.setDog(dog);
            FuErDai fuErDai = new FuErDai();
            fuErDai.setCommandOne(playWithServantCommand);
            fuErDai.setCommandTwo(playWithDogCommand);
            fuErDai.playOne();
            fuErDai.playTwo();
        }
    }
    

    输入/输出:

    洗澡!
    如果你追到我,我就和你嘿嘿嘿!
    汪汪汪!

    这就是命令模式,作为富二代的你,只需关心发出命令,可以随时修改命令的执行者,并且不用关心其如何执行。

    使用命令模式重构之后,就再也不用担心富二代喜新厌旧了。

    哪天你想要和你的基友玩耍了,不用修改你本身的代码,只需新建一个PlayWithJiYouCommand类,新建对象并set进富二代对象的其中一个command属性中即可。

    你按下其中一个按钮,就可以和基友疯狂地玩耍了。

    当然,如果高富帅有很多仆人,可以在FuErDai中添加一个数据结构用于保存所有的命令,而不必替换。

    你感觉命令模式很好用,并且这个命名很符合你高富帅的身份,你笑得像个200斤的孩子。

    笑得像个200斤的孩子

    三:再理解

    1. 命令模式可以做到调用者和被调用者之间的解耦。
    2. 对修改关闭,对增加开放,只需增加命令类,而无需修改调用者代码。
    3. 就像家里的灯,你真正持有的是开灯的那个开关。灯坏了可以直接换,甚至可以把灯换成电扇。有了开关,你无需学习如何操控更换后的电器。

    相关文章

      网友评论

        本文标题:学好设计模式防被祭天:命令模式

        本文链接:https://www.haomeiwen.com/subject/rnwadxtx.html