十二:命令模式(人员解耦和)

2019-08-09 00:00:00 模式 命令 人员

案例:“由于公司的制度不完善,人员缺乏,导致业务人员一有什么事情,全部都来找自己。本来自己就已经身兼数职,现在还要每天面临业务员们的各种炮轰,实在是有点受不了啊。”

于是,小左想着想着,就打算将这个问题写出来,看能不能从代码里找到现实中解决问题的答案。在键盘前犹豫片刻后,小左先把自己用JAVA代码解释了一遍。

《十二:命令模式(人员解耦和)》

package com.command;

public class Programmer {

    private String name;

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

    public String getName() {
        return name;
    }
    
    public void handleDemand(){
        System.out.println( name + "处理新需求");
    }
    
    public void handleBug(){
        System.out.println( name + "处理bug");
    }
    
    public void handleProblem(){
        System.out.println( name + "处理线上问题");
    }
    
}

《十二:命令模式(人员解耦和)》

                看着自己的功能,小左不禁感叹,“自己就是一个机器啊”。感叹之余,小左又继续摸着下巴思考了片刻,写出了业务人员的类。

《十二:命令模式(人员解耦和)》

package com.command;

public class Salesman {

    private String name;

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

    public String getName() {
        return name;
    }
    
    public void putDemand(Programmer programmer){
        System.out.println( "业务员" + name + "提出新需求");
        programmer.handleDemand();
    }
    
    public void putBug(Programmer programmer){
        System.out.println( "业务员" + name + "提出bug");
        programmer.handleBug();
    }
    
    public void putProblem(Programmer programmer){
        System.out.println( "业务员" + name + "提出线上问题");
        programmer.handleProblem();
    }
}

《十二:命令模式(人员解耦和)》

                最后,小左就干脆拿今天的工作为例,写了一下一天的工作。

《十二:命令模式(人员解耦和)》

package com.command;

public class Work{

    public static void main(String[] args) {
        Programmer xiaozuo = new Programmer("小左");
        
        Salesman salesmanA = new Salesman("A");
        salesmanA.putDemand(xiaozuo);
        
        Salesman salesmanB = new Salesman("B");
        salesmanB.putDemand(xiaozuo);
        
        Salesman salesmanC = new Salesman("C");
        salesmanC.putBug(xiaozuo);
        
        Salesman salesmanD = new Salesman("D");
        salesmanD.putProblem(xiaozuo);
    }
    
}

《十二:命令模式(人员解耦和)》

《十二:命令模式(人员解耦和)》
                看着运行的结果,小左不禁发出一声冷笑,暗自为自己感到悲哀。仔细的看着代码,小左开始一一分析自己的悲哀。

                1,业务员和自己的耦合度太高,导致每个业务人员都可以直接命令自己,这导致自己心里很不爽。

                2,由于自己本身只是个程序猿,所以自己对公司的业务并不是特别擅长,很难给任务制定优先级,给任务排序,自己所擅长的还是码代码,结果造成的后果就是经常按时完成了一个不重要的任务,但不小心却将很重要的任务向后推迟了,如果业务人员告状,自己可能就要挨批。可是这么多业务人员提问题,到底谁轻谁重,我哪知道。

                3,如果任务堆积过多的时候,自己有时会忘记其中的一个甚至几个,人毕竟精力有限,如果任务多了,难免会出现这种情况,结果有的业务人员就直接告状到老板那里,实在是憋屈。

                4,由于任务是随即产生的,业务人员什么时候想到任务就随时给自己,所以没有一个整体的规划,很容易导致自己加班。        

                “看来,说来说去,还是业务人员跟我太紧密了啊。”小左叹气道。

                由于小左最近在研究设计模式,所以就想到了是不是可以用设计模式来解决自己和业务人员之间的问题。于是小左开始在度娘上面找寻适合的设计模式。

                “有了,命令模式应该是可以解决的,看它的定义好像挺符合的”。电脑前的小左突然一拍大腿,大叫道。

                定义:在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)。

                看着这个定义,小左心中想道:“命令模式中所说的两个角色,不正是我和业务人员吗。业务人员是行为请求者,他们请求我,噢,不,应该说命令我产生编码,修改bug和处理线上问题的行为,而我就去实现或者说执行这些行为。况且,看命令模式的定义,还可以支持记录,我正需要这个记录啊,否则每次任务太多,忘了哪个任务,都要我挨批。”

               于是小左迫不及待的开始研究命令模式的类图,试图从中找到解决的办法。

 《十二:命令模式(人员解耦和)》

               “不对啊,这个模式当中好像有一个invoker(调用者),这会是什么样的人呢?”小左看着命令模式的类图,不禁有些疑惑。

               小左看到类图的时候,一下就看出自己便是Receiver,而业务人员便是Client,可是这个Invoker是什么呢?从类图上看,小左知道,这应该是一个特别的人,它是专门用来记录业务人员提出的需求,bug以及线上问题等等,并且还要负责通知我完成各个任务。

               “我擦,这不就是产品经理(ProductManager)吗。”小左脑子中忽然闪出这个名词。

               “这下有了,产品经理负责接收业务人员的各个任务,然后过滤和排好优先级以后再交代给我完成,并且他可以记住所有接受过的任务,这样我也不会再忘记了,因为有他提醒着我呢。”

               说干就干,小左立马就开始尝试用命令模式去处理自己和业务人员的紧耦合问题,而解决这个问题最直接的手段就是添加了一个产品经理,并且将各个任务都抽象成一个对象,这样产品经理就可以管理这些对象了,或者也可以说是管理这些任务。

               小左觉得自己的类是不需要变化的,主要变化的是要添加一组抽象的行为对象,还有改变客户端调用的方式,也就是业务人员的类。

               那么首先是添加命令接口和具体的命令,不过这里似乎叫任务(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;
    
    public ProductManager() {
        super();
        taskList = new ArrayList<Task>();
    }
    
    //接受一个任务
    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();
        }
        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 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;

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

    public String getName() {
        return name;
    }
    
    public void putDemand(ProductManager productManager,Programmer programmer){
        System.out.println( "业务员" + name + "提出新需求");
        productManager.receive(new Demand(programmer));
    }
    
    public void putBug(ProductManager productManager,Programmer programmer){
        System.out.println( "业务员" + name + "提出bug");
        productManager.receive(new Bug(programmer));
    }
    
    public void putProblem(ProductManager productManager,Programmer programmer){
        System.out.println( "业务员" + name + "提出线上问题");
        productManager.receive(new Problem(programmer));
    }
}

《十二:命令模式(人员解耦和)》

                “这下好了,业务人员再也不能让我直接去写代码了,必须得发给产品经理,再让产品经理分配给我。这下我看下我的工作应该是什么样子了。”

《十二:命令模式(人员解耦和)》

package com.command;

public class Work {

    public static void main(String[] args) {
        Programmer xiaozuo = new Programmer("小左");
        ProductManager productManager = new ProductManager();
        
        Salesman salesmanA = new Salesman("A");
        Salesman salesmanB = new Salesman("B");
        Salesman salesmanC = new Salesman("C");
        Salesman salesmanD = new Salesman("D");
        
        salesmanA.putDemand(productManager, xiaozuo);
        salesmanB.putDemand(productManager, xiaozuo);
        salesmanB.putBug(productManager, xiaozuo);
        salesmanC.putDemand(productManager, xiaozuo);
        salesmanC.putProblem(productManager, xiaozuo);
        salesmanD.putDemand(productManager, xiaozuo);
        
        System.out.println("第一天产品经理分配任务");
        productManager.assign();
        productManager.printTaskList();
        System.out.println("第二天产品经理分配任务");
        productManager.assign();
        productManager.printTaskList();
    }
    
}

《十二:命令模式(人员解耦和)》

《十二:命令模式(人员解耦和)》
                  小左看着运行结果和代码开始思考,“这样和标准的命令模式几乎已经一样了,只是设计模式还是不能死搬硬套啊,有些地方还是有些别扭。比如业务人员不应该再依赖于程序猿,业务人员应该只依赖产品经理就可以了,他不应该负责把任务对应给我,而应该是产品经理决定某一个任务由谁去处理的。我还得把这个地方再改一下,要不按照现在的样子,业务人员还是能把任务分配到我身上。不过目前的好处是,我不需要当时就做了,而是等着产品经理分配。从结果就看出来了,刚才是业务人员发个任务我就做一个,现在是产品经理分配了一天的任务,然后一起做。”

                  “按照刚才想的思路,产品经理应该要认识所有的程序猿,而且还要提供一个可以选择程序猿的方法。”小左拖着下巴喃喃的说道,说完以后就开始修改刚才的代码。

《十二:命令模式(人员解耦和)》

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();
        }
        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("----------------------------------");
    }
}

《十二:命令模式(人员解耦和)》

                “这下业务人员就不需要认识我了,哈哈。”小左边YY着有了产品经理的美好生活,一边将业务人员的类改成了下面的样子。

《十二:命令模式(人员解耦和)》

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();
    }
    
}

《十二:命令模式(人员解耦和)》

                “哈哈,这下业务人员彻底不认识我了,只认识产品经理,只不过由于公司就我一个程序员,所以产品经理那个平均的循环分配算法,分来分去还是分给我一个人了。不过这个也没办法,公司不招人,我也说了不算啊,而且小甜甜还没熟悉好公司业务呢,也帮不了我。” 

                虽然最终这些任务都还是小左去做,但是这下小左不用加班了,一天最多四个任务,就算完不成临时需要加班,那也是小左编码速度的问题,心里相对会舒服很多,不至于太憋屈。毕竟自己没完成,那加会儿班也会心甘情愿。

                这会小左心情好了,心情一好,小左竟然开始动手画起刚才的类图来了。

《十二:命令模式(人员解耦和)》

                小左看着自己的作品,自言自语道:“虽然这个实际的类图比命令模式的原版类图稍微多了一层关系,就是ProductManager和Programmer的关联关系,但是其它的可是和命令模式一模一样啊,而且很明显,业务人员(Salesman)和我们程序猿(Programmer)之间没有关联的关系了。”

                “看来我们公司应该招一个产品经理了啊,还得多招几个程序猿,这样我的工作就简单的多了。公司和程序倒是挺相似的,程序是需要多个类之间相互协作,公司也是一样啊,需要每个员工都分工协作,这样就不需要一些人身兼数职了,简直是累死个人啊!”

                “博客今天就先不写了,稍微总结一下,明天好去给小甜甜说一下我今晚的发现,顺便给她讲讲命令模式。这下又可以在她面前露一手了,估计这样下去,她早晚会爱上我啊。啊,哈哈。。”小左又开始YY了。

                不过为了能给小甜甜留一个好印象,小左可是煞费心思啊,这不,他又开始总结起来了。

                “为了减轻自己的负担,我添加了一个产品经理,并且还将任务抽象成类,这样确实解决了我的很多问题,我得稍微总结下。”

                1,程序猿和业务员解耦,不直接打交道。

                2,产品经理分担了程序猿的很多潜在任务,比如制定任务优先级,先做哪个后做哪个。

                3,程序猿不至于忘掉其中一个任务,因为产品经理那有任务列表的。

                4,任务有规划的完成,不至于加班或者说加班太频繁。

               “不过要是给小甜甜解释,这样还不够啊,得专业一点,才好忽悠啊。”小左暗暗的点头道。

                用编程的语言来解释命令模式的使用场景或者说可以解决的问题,就是下面几点。

                1,希望将行为请求者和行为实现者解耦,不直接打交道。

                2,希望分离掉行为请求者一部分的责任,行为请求者只需要将命令发给调用者,不再主动的去让行为实现者产生行为,符合单一职责原则。

                3,希望可以控制执行的命令列表,方便记录,撤销/重做以及事务等功能。

                4,期待可以将请求排队,有序执行。

                5,希望可以将请求组合使用。

                “不过上面我总结的第五点在例子当中没有体现,不过这个也好办,其实就相当于是任务可以组合,比如一个线上的问题(Problem)很可能伴随着一个bug(Bug),这样的话可以做一个组合的任务,既要处理线上的问题同时也要处理Bug。”

                “装X就要装到底啊,命令模式的优点和缺点都有哪些,我也得给总结出来,别到时候在小甜甜面前掉链子。”小左一甩自己的中分头说道。

                1,最大的优点,就是将行为请求者和行为实现者解耦。

                2,命令的添加特别方便,并且可以方便的制定各种命令和利用现有命令组合出新的命令。
                3,如果针对每一类具有共同接口的接受者制作一个调用者,可以控制命令的执行情况。

                “命令模式的缺点应该和大部分设计模式一样,会增加系统的复杂性,这里的复杂性应该主要指的是类的数量,这个倒是好理解,看一下上面的例子就知道多了很多类。”

相关文章