java-设计模式-命令模式

设计模式-命令模式
    命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,可撤销的操作。
    行为的请求者 和 行为的执行者 解耦
    典型例子:服务员(调用者 invoker)、厨师(接收者),菜单(命令)

命令模式的结构也很简单,先认识下面的三个角色:
    invoker 调用命令的人
    command、ConcreteCommand 命令接口和具体的命令
    receiver 执行命令的人

来个类图:

命令的执行者是Receiver,所以命令类上有执行者。

客户端要做的事:

1:只需要对invoker进行发布命令

2:设置命令的接收者(其实这个可以对客户端也透明的)

 

下面用一个例子来解释命令模式。

1:项目经理接收客户的需求(命令)

2:工人(程序员)执行开发命令

3:命令有:开发产品A、开发产品B、进行开会

先来看命令接口:

这个命令定义了命令的执行者和命令名(为了方便我们知道调用了哪个命令)


/**
 * 命令模式- 命令,这里是一个任务
 */
public abstract class ICommand {
    /**
     * 命令的执行者
     */
    protected WorkerReceiver workerReceiver;

    private String commandName;

    /**
     * 构造方法
     */
    ICommand(WorkerReceiver workerReceiver,String commandName){
        this.workerReceiver = workerReceiver;
        this.commandName = commandName;
    }

    /**
     * 定义一个执行方法
     */
    abstract void execute();

    public String getCommandName() {
        return commandName;
    }

    public void setCommandName(String commandName) {
        this.commandName = commandName;
    }
}

具体的命令:

生产产品A的命令:

生产产品A需要的时间是10,执行命令时,让在其它线程上执行。


public class ProductACommand extends ICommand {
    private int timeSecond = 10;//生产这个产品需要10秒
    /**
     * 构造方法
     *
     * @param workerReceiver
     */
    ProductACommand(WorkerReceiver workerReceiver) {
        super(workerReceiver,"生产产品A命令");
    }

    @Override
    void execute() {
        new Thread(()->{workerReceiver.work(timeSecond);}).start();
    }
}

生产产品B的命令:

和生产产品A的命令相似


public class ProductBCommand extends ICommand {
    private int timeSecond = 20;//生产这个产品需要20秒
    /**
     * 构造方法
     *
     * @param workerReceiver
     */
    ProductBCommand(WorkerReceiver workerReceiver) {
        super(workerReceiver,"生产产品B命令");
    }

    @Override
    void execute() {
        new Thread(()->{workerReceiver.work(timeSecond);}).start();
    }
}

开会的命令:

开会命令,调用的接收者的是开会的方法。


/**
 * 开会的命令
 */
public class MeetingCommand extends ICommand {
    private int timeSecond = 15;//开会需要15秒
    /**
     * 构造方法
     *
     * @param workerReceiver
     */
    MeetingCommand(WorkerReceiver workerReceiver) {
        super(workerReceiver,"开会命令");
    }

    @Override
    void execute() {
        //另起线程
        new Thread(()->{workerReceiver.meeting(timeSecond);}).start();
    }
}

员工类(命令执行者):

有两个动作,一个是进行工作,另外一个是进行开会,对应上诉的工作和开会的命令。

有个状态 freeStatus,只有在空闲状态,才可以进行工作或开会。


/**
 * 员工 - 命令模式的receiver 执行任务的人
 * 这里其实可以把这个对象抽象化
 */
public class WorkerReceiver {
    private boolean freeStatus = true;//是否在空闲状态
    private String name;
    WorkerReceiver(String name){
        this.name = name;
    }
    /**
     * 进行工作 需要加锁
     */
    public synchronized void work(int second){
        System.out.println("********接受者"+name+": 进行生产 需要"+second+"秒");
        freeStatus = false;
        try {
            Thread.sleep(second * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("接受者"+name+" 结束生产 释放锁");
        freeStatus=true;
    }

    /**
     * 进行开会 需要加锁
     */
    public synchronized void meeting(int second){
        freeStatus = false;
        System.out.println("*********接受者"+name+":进行会议 需要开"+second+"秒");
        try {
            Thread.sleep(second * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("接受者"+name+":结束会议 释放锁");
        freeStatus = true;
    }

    //

    public boolean isFreeStatus() {
        return freeStatus;
    }

    public void setFreeStatus(boolean freeStatus) {
        this.freeStatus = freeStatus;
    }
}

研发经理类(命令调用者invoker)

初始化时默认添加4个 命令接收者(程序员)

对外提供三个动作: 1、发命令 2、加人 3、找到一个空闲的接收者(只有找到空闲的接收者,才能发指令)


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

/**
 * 研发经理-命令模式的调用者
 */
public class ManagerInvoker {

    private  List<WorkerReceiver> workerReceivers = new ArrayList<>();

    public ManagerInvoker() {
        System.out.println("初始化执行者");
        for (int i=0;i<4;i++){
            workerReceivers.add(new WorkerReceiver(i+""));
        }
    }

    /**
     * 设置命令
     * @param iCommand 命令
     */
    public void setCommand(ICommand iCommand){
        iCommand.execute();
    }

    /**
     * 添加接收者
     */
    public void addReceiver(){
        System.out.println("添加工作者成功");
        workerReceivers.add(new WorkerReceiver(workerReceivers.size()+""));
    }

    /**
     * 获取空闲的工作者
     */
    public WorkerReceiver getFreeReceiver(){
        boolean workFlag = false;
        for (int i=0;i<workerReceivers.size();i++){
            if(workerReceivers.get(i).isFreeStatus()){
                workFlag = true;
                System.out.println("worker "+ i +" 正在空闲");
                return workerReceivers.get(i);
            }
        }
        if(!workFlag){
            System.out.println("没有工人是空闲的 这里可以弄个等待队列,这里暂时不弄");
        }
        return null;
    }

}

测试方法:

 public static void main(String[] args) {
        ManagerInvoker managerInvoker = new ManagerInvoker();
        Scanner scanner = new Scanner(System.in);
        String text = null;
        System.out.println("请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人");
        while ((text = scanner.next())!= null){
            switch (text){
                case "1":{
                    WorkerReceiver workerReceiver = managerInvoker.getFreeReceiver();
                    if(workerReceiver != null){
                        managerInvoker.setCommand(new ProductACommand(workerReceiver));
                    }
                }
                    break;
                case "2":{
                    WorkerReceiver workerReceiver = managerInvoker.getFreeReceiver();
                    if(workerReceiver != null){
                        managerInvoker.setCommand(new ProductBCommand(workerReceiver));
                    }
                }
                    break;
                case "3":{
                    WorkerReceiver workerReceiver = managerInvoker.getFreeReceiver();
                    if(workerReceiver != null){
                        managerInvoker.setCommand(new MeetingCommand(workerReceiver));
                    }
                }
                    break;
                case "0":
                    managerInvoker.addReceiver();
                    break;
            }
            System.out.println("请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人");
        }
    }

测试结果:

初始化执行者
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
2
worker 0 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者0: 进行生产 需要20秒
2
worker 1 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者1: 进行生产 需要20秒
3
worker 2 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
*********接受者2:进行会议 需要开15秒
1
worker 3 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者3: 进行生产 需要10秒
2
没有工人是空闲的 这里可以弄个等待队列,这里暂时不弄
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
0
添加工作者成功
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
2
worker 4 正在空闲
请输入命令 1 生产产品A ; 2 生产产品B ; 3 进行开会; 0 添加工人
********接受者4: 进行生产 需要20秒

这个例子模拟了我们程序开发过程的一个现象:

大Boss对研发经理说:你们今天需要给我 把XXX 做完

产品经理说:XXX分为 1,2,3 部分,每个部分完成需要 xxx 时间,我们的员工现在都没有空。

大Boss说:让大家加加班呀

产品经理说:我们现在每天都在加班了,你看看我们的周报。。。。

大Boss看看周报,发现员工们真的很晚才下班,咦,怎么这么多人在开会,发费好多时间呢

大Boss说:这会议。。。(说不下去了)

产品经理说:这些会议都是您下发的通知,说一定要全员到齐,不然扣工资的,但是很多会议内容都是无意义的。

大Boss也尴尬了,说:以后提高会议效率,只让相关的人去听

产品经理说:嗯嗯,是的。但是我们也就只有这几个人,真的没办法在这个月完成那么多工作量。

大Boss说:加人!!

产品经理笑了。

 

看看,这个命令模式,帮助产品经理说服Boss加人和减少会议次数,还真是有用的模式呀。但是真正能为低下员工扛的产品经理还是比较少的,如果你遇到了,那么请你珍惜。

说远了,设计模式没有固定的形态,放心大胆的去用,被坑过才会更爱。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章