设计模式(三)命令模式

203 阅读4分钟

今天在面试别人的过程,问到了设计模式,他说命令模式,what!!我没有听过,我只能强装淡定,问了问他,回来赶紧翻翻书,补一补。

命令模式

日常背书:命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。先来讲个故事吧,再来说说自己的理解。

板面的故事

板面相信大家都吃过吧,今天讲的是公司那边的一家板面店, 我们皮皮家族经常去吃,这家店中只有老板和老板娘,每次都记不住我们点了什么,也记不住点餐的顺序,经常发生的场景就是,老板端着一碗板面说兄弟的板面加肠好了,你满面黑线的说,我点的是盖饭。下面我们用代码来描述下这个板面的业务模型。

板面1.0:紧耦合

板面店:

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/21
 * @description
 */
public class Noodler {

    public void makeNoodle(){
        System.out.println("兄弟,你的板面加肠加蛋!");
    }

    public void makeRice(){
        System.out.println("兄弟,你的盖饭!");
    }
}

皮皮家族来到了板面店

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/21
 * @description
 */
public class Demo {

    public static void main(String[] args){
        Noodler noodler = new Noodler();
        //皮皮家族来到了板面店,点了3个板面,2个盖饭
        noodler.makeNoodle();
        noodler.makeRice();
        noodler.makeNoodle();
        noodler.makeRice();
        noodler.makeNoodle();
    }
}

效果:

板面1.0的效果

板面1.0,描述了目前板面店的业务模型,实现很简单,但是暴露了很多问题。

从现实业务上来看

我们作为消费者直接与厨师(做饭也是老板和老板娘做的)交互,他们无法专注于自己的本质工作(做饭),需要记住每个人点的东西,每个点餐的顺序,就造成了目前的情况,不是看你点了什么,而是看他们做了什么,你点的东西可能已经没有原材料做不了了,但是也无法及时通知你,你想换一种饭,他可能无法顾及你的需求,这种不单一的职责,让他们无法同时顾及做饭与招呼客人点东西,两边的工作都可能做不好

从代码上来看

这种设计,我们作为行为请求者,板面店作为行为执行者,两者是紧耦合,对于一些简单的场景,这么做比较合适,请求者直接与执行者交互,但在某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种紧耦合的设计就不是很合适

板面2.0:使用命令模式为板面店解耦

我们皮皮家族一直在讨论板面店的问题在,认为关键点就是把招呼客人与做饭分开,在我们的代码中,修改老板与老板娘的职责,让他们只负责做饭:

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class Maker {

    public void make(String s){
        System.out.println("兄弟,我只负责做饭!我正在" + s);
    }

    //老板、老板娘查看厨房信息,看看客人点的是否能做
    public boolean getInfo(String s){
        if ("板面".equals(s)){
            return true;
        }else if ("盖饭".equals(s)){
            return true;
        }else if ("韭菜水饺".equals(s)){
            System.out.println("韭菜没有了,不要让客人点韭菜水饺了!");
            return false;
        }else {
            return true;
        }
    }
}

为板面店做一个订单系统:

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public interface Command {

    /**
     * 订单系统通知老板、老板娘做饭
     */
    void execute();

    /**
     * 获取厨房信息
     * @return
     */
    String getMakeInfo();
}

在订单系统中,添加一个板面的信息

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class NoodleCommand implements Command {

    private Maker maker;

    public NoodleCommand(Maker maker) {
        this.maker = maker;
    }

    @Override
    public void execute() {
        System.out.println("订单系统通知老板,做一碗板面!");
        maker.make("做板面");
    }

    @Override
    public String getMakeInfo() {
        if (maker.getInfo("板面")){
            System.out.println("老板查看厨房信息!");
            System.out.println("发现有材料,可以做板面!");
            System.out.println("通过订单系统,通知服务员招呼客人说:可以点板面!");
            return "老板,给做一碗板面!";
        }else {
            System.out.println("没有材料,做不了板面了!");
            return "NULL";
        }
    }
}

为板面店招聘一个服务员,专门负责招呼客人:

package edu.design.pattern.command;

import java.util.ArrayList;
import java.util.List;

/**
 * 服务员类
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class Waiter {

    //服务员控制订单系统
    private List<Command> commandList = new ArrayList<>();

    //服务员招呼客人,把客人的点餐情况输入点餐系统
    //今天没有韭菜了,韭菜水饺点不了了
    public void setCommand(Command command){
        if (command.getMakeInfo().equals("NULL")){
            System.out.println("韭菜没有了,包不了韭菜水饺了!");
        }else {
            commandList.add(command);
        }
    }

    //服务员在订单系统中通知老板、老板娘做饭
    public void notifyMaker(){
        if (commandList.size() == 0){
            System.out.println("暂时没有客人点餐!");
        }
        for (Command command : commandList){
            command.execute();
        }
    }
}

板面店重新开张,皮皮家族又来了:

package edu.design.pattern.command;

/**
 * @author ZhaoWeinan
 * @date 2018/3/22
 * @description
 */
public class CommandDemo {

    public static void main(String[] args){
        //老板登场
        Maker maker = new Maker();
        //服务员登场,招呼客人
        Waiter waiter = new Waiter();
        //服务员查看订单系统通知,是否可以点板面
        Command command = new NoodleCommand(maker);
        //服务员通过订单系统点了一份板面
        waiter.setCommand(command);
        //服务员点击确认,订单系统通知老板做饭
        waiter.notifyMaker();
    }
}

效果:

板面2.0的效果

通过这次板面2.0的升级,解决了1.0中的这些问题,板面的故事,讲完了,让我们来总结一下吧。

总结

从上面的故事我们来总结一下

需要解决的问题

行为请求者与执行者之间紧耦合的设计,对于一些简单的场景,这么做比较合适,请求者直接与执行者交互,但在某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种紧耦合的设计就不是很合适。

解决的方式

命令模式制定了三个主要的角色:命令执行对象receiver、 命令对象command、命令请求的入口invoker,通过请求者通过命令请求的入口把命令传递给接受执行者进行命令的执行,来把请求→执行的过程进行了解耦

优点

1、降低了系统耦合度
2、可以比较容易的把命令记入日志
3、允许命令接收方决定是否接受命令
4、新的命令可以很容易添加到系统中去

命令模式就为大家说到这里,欢迎大家来交流,指出文中一些说错的地方,让我加深认识。 谢谢大家!