手记

java设计模式-命令模式

命令模式-是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
结构图如下:

以街边小吃摊为例:顾客对摊主下单,摊主处理顾客的请求。但是如果要对行为进行“记录、撤销/重做、事务”等处理,摊主就会有些忙不过来,甚至出错。此时这种无法抵御变化的紧耦合是不合适的。
将“行为请求者”与“行为实现者”解耦,将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)。
我们引入一个中间的对象,以上述为例,建立一个帮工对象。顾客对帮工进行请求动作,帮工负责对顾客的需求排队和收集,然后对摊主下单,这样摊主就可以专心工作,而不必关心其他。
代码结构,首先需要一个命令类,声明执行操作的接口(需求接口):

package com.hy.order;

public abstract class Command {
    protected Recevier recevier;

    public Command(Recevier recevier){
        this.recevier = recevier;
    }
    abstract public void execute();
}

ConcreteCommand类,具体的请求实现类(具体的需求),将一个请求者对象绑定一个动作,调用接收者相应的操作,实现execute():

package com.hy.order;

public class ConcreteCommand extends Command {

    public ConcreteCommand(Recevier recevier) {
        super(recevier);
    }

    @Override
    public void execute() {
        return recevier.execute() ;
    }

}

Invoker类,要求该命令执行请求(帮工):

package com.hy.order;

public class Invoker {
    private Command commands;

    //设置订单
    public void setOrder(Command command){
        this.command=command;
    }

        public void executeCommand() {
        command.execute() ;
    }
}

Receiver(摊主)类,实施或执行一个操作:

package com.hy.order;

public class Receiver{
    public void action(){
            System.out.println("执行操作");
    }
}

最后是调用的客户端:

package com.hy.order;

public class Client {
    public static void main(String[] args) {
        Receiver receiver= new  Receiver();
        Command command = new Command(receiver);
        Invoker invoker = new  Invoker();
        i.setOrder(command);
        i.executeCommand();
    }

}

上面提到将客户的需求排队,所以invoke的类中,可以添加一个command的list去存储一系列的请求,然后在execute中是用foreach去逐个实现请求。

下面以开发人员为例,项目经理负责分配任务,而用户负责提供需求,郑哥哥代码的结构如下。
添加命令接口和具体的命令,不过这里似乎叫任务(Task)更合适:

package com.command;

public interface Task {

    void handle();

}

几个具体的任务一并给出,而且具体的命令是要关联一个接受者的,而接受者正是开发人员,也就是Programmer:

package com.command;

public class Demand implements Task{

    private Programmer programmer;

    public Demand(Programmer programmer) {
        super();
        this.programmer = programmer;
    }

    public void handle() {
        programmer.handleDemand();
    }

    public String toString() {
        return "Demand [programmer=" + programmer.getName() + "]";
    }

}
package com.command;

public class Bug implements Task{

    private Programmer programmer;

    public Bug(Programmer programmer) {
        super();
        this.programmer = programmer;
    }

    public void handle() {
        programmer.handleBug();
    }

    public String toString() {
        return "Bug [programmer=" + programmer.getName() + "]";
    }

}
package com.command;

public class Problem implements Task{

    private Programmer programmer;

    public Problem(Programmer programmer) {
        super();
        this.programmer = programmer;
    }

    public void handle() {
        programmer.handleProblem();
    }

    public String toString() {
        return "Problem [programmer=" + programmer.getName() + "]";
    }

}

这些任务包括了新的需求,bug和线上问题等等这些种类,而接受者正是开发人员,所以handle方法里直接处理这个任务。
下面是最主要的角色,产品经理:

package com.command;

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

public class ProductManager {

    private static final int TASK_NUMBER_IN_DAY = 4;//一天最多分派掉四个任务,多了推到第二天

    private List<Task> taskList;

    private List<Programmer> programmerList;//产品经理应该认识所有的程序猿

    private int currentIndex;

    public ProductManager(Programmer... programmers) {
        super();
        if (programmers == null || programmers.length == 0) {
            throw new RuntimeException("产品经理手下没有程序员,任务分配不出去,无法正常工作!");
        }
        taskList = new ArrayList<Task>();
        programmerList = Arrays.asList(programmers);
    }

    //接受一个任务
    public void receive(Task task){
        taskList.add(task);
    }

    public void assign(){
        Task[] copy = new Task[taskList.size() > TASK_NUMBER_IN_DAY ? taskList.size() - TASK_NUMBER_IN_DAY : 0];
        for (int i = 0; i < TASK_NUMBER_IN_DAY && i < taskList.size(); i++) {
            taskList.get(i).handle();
        }
         //数组间的复制,参数1是源数组,参数2是起始位置,参数3是目标数组,参数4是目标数组放置的起始位置,参数5是复制数据长度。这个方法也可以用来复制自身,原理是先生成一个临时数据作为源数组。
        System.arraycopy(taskList.toArray(), TASK_NUMBER_IN_DAY > taskList.size() ? taskList.size() : TASK_NUMBER_IN_DAY, copy, 0, copy.length);
        taskList = Arrays.asList(copy);
    }
    //产品经理可以选择程序猿,简单的循环选取。
    public Programmer chooseProgrammer(){
        return programmerList.get(currentIndex == programmerList.size() ? 0 : currentIndex++);
    }

    public void printTaskList(){
        if (taskList == null || taskList.size() == 0) {
            System.out.println("----------当前无任务--------");
            return;
        }
        System.out.println("---------当前剩下的任务列表--------");
        for (Task task : taskList) {
            System.out.println(task);
        }
        System.out.println("----------------------------------");
    }
}

用户类(业务员):

package com.command;

public class Salesman {

    private String name;

    private ProductManager productManager;

    public Salesman(String name) {
        super();
        this.name = name;
    }

    public Salesman(String name, ProductManager productManager) {
        super();
        this.name = name;
        this.productManager = productManager;
    }

    public void putDemand(){
        System.out.println( "业务员" + name + "提出新需求");
        productManager.receive(new Demand(productManager.chooseProgrammer()));
    }

    public void putBug(){
        System.out.println( "业务员" + name + "提出bug");
        productManager.receive(new Bug(productManager.chooseProgrammer()));
    }

    public void putProblem(){
        System.out.println( "业务员" + name + "提出线上问题");
        productManager.receive(new Problem(productManager.chooseProgrammer()));
    }

    public String getName() {
        return name;
    }

    public ProductManager getProductManager() {
        return productManager;
    }

    public void setProductManager(ProductManager productManager) {
        this.productManager = productManager;
    }
}

测试类:

package com.command;

public class Work {

    public static void main(String[] args) {
        Programmer xiaozuo = new Programmer("小左");
        ProductManager productManager = new ProductManager(xiaozuo);

        Salesman salesmanA = new Salesman("A",productManager);
        Salesman salesmanB = new Salesman("B",productManager);
        Salesman salesmanC = new Salesman("C",productManager);
        Salesman salesmanD = new Salesman("D",productManager);

        salesmanA.putDemand();
        salesmanB.putDemand();
        salesmanB.putBug();
        salesmanC.putDemand();
        salesmanC.putProblem();
        salesmanD.putDemand();

        System.out.println("第一天产品经理分配任务");
        productManager.assign();
        productManager.printTaskList();
        System.out.println("第二天产品经理分配任务");
        productManager.assign();
        productManager.printTaskList();
    }

}

总结:
1,业务人员(Salesman)和开发人员(Programmer)之间没有关联,即解耦。
2,产品经理分担了程序猿的很多潜在任务,比如制定任务优先级,先做哪个后做哪个。
3,开发人员不至于忘掉其中一个任务,因为产品经理那有任务列表的。
4,任务有规划的完成,每天有对应的工作量。

命令模式的使用场景或者说可以解决的问题,就是下面几点。
1,希望将行为请求者和行为实现者解耦,不直接打交道。
2,希望分离掉行为请求者一部分的责任,行为请求者只需要将命令发给调用者,不再主动的去让行为实现者产生行为,符合单一职责原则。
3,希望可以控制执行的命令列表,方便记录,撤销/重做以及事务等功能。
4,期待可以将请求排队,有序执行。
5,希望可以将请求组合使用。

优点:
1.将行为请求者和行为实现者解耦。
2.命令的添加特别方便,并且可以方便的制定各种命令和利用现有命令组合出新的命令。
3.如果针对每一类具有共同接口的接受者制作一个调用者,可以控制命令的执行情况。
缺点:会增加系统的复杂性,主要指的是类的数量。

2人推荐
随时随地看视频
慕课网APP