命令模式

Posted hhhshct

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了命令模式相关的知识,希望对你有一定的参考价值。

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

  在网上找了个命令模式的标准类图

技术分享图片

  命令模式中有五种角色,分别是:

  1、抽象命令(Command):定义命令的接口,声明执行的方法

  2、具体命令(ConcreteCommand):具体命令,实现要执行的方法,它通常是“虚”的实现;通常会有接收者,并调用接收者的功能来完成命令要执行的操作。

  3、接收者(Receiver):真正执行命令的对象。任何类都可能成为一个接收者,只要能实现命令要求实现的相应功能。

  4、调用者(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

  5、客户端(Client):命令由客户端来创建,并设置命令的接收者。

  我们以顾客到餐馆吃饭为例,顾客先要点单,然后交与服务员,服务员拿着单子去找后厨按要求做菜,这便是命令模式的体现,实现了顾客和厨师的解耦,在这个过程中顾客便是我们的client,单子便是具体的command,服务员是invoker,厨师就是具体的实现者receiver。下面是demo

  首先是makefood接口,相当于抽象命令

package io.powerx.command;

public interface MakeFood {

    void execute();
}

  三种实现类,制作不同的食物,在实现类中持有我们的cook,调用cook的方法完成制作

package io.powerx.command;

public class MakeBread implements MakeFood {

    private Cook cook;
    
    public MakeBread(Cook cook) {
        super();
        this.cook = cook;
    }

    @Override
    public void execute() {
        
        cook.makeBread();
    }

}
package io.powerx.command;

public class MakeRoast implements MakeFood {

    private Cook cook;
    
    public MakeRoast(Cook cook) {
        super();
        this.cook = cook;
    }

    @Override
    public void execute() {
        
        cook.makeRoast();
    }

}
package io.powerx.command;

public class MakeChicken implements MakeFood {

    private Cook cook;
    
    public MakeChicken(Cook cook) {
        super();
        this.cook = cook;
    }

    @Override
    public void execute() {
        
        cook.makeChicken();
    }

}

  厨师类

  

package io.powerx.command;

public class Cook {

    private String name;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    public void makeBread() {
        System.out.println(name +"  make  bread"); 
    }
    
    public void makeRoast() {
        System.out.println(name + " make roast"); 
    }
    
    public void makeChicken () {
        System.out.println(name + " make chicken");
    }
}

  服务员类,它持有一个Makefood的list,用来存储顾客的点单信息

package io.powerx.command;

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

public class Waiter {
    
    private String name;
    

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<MakeFood> getList() {
        return list;
    }

    public void setList(List<MakeFood> list) {
        this.list = list;
    }

    public Waiter(String name) {
        super();
        this.name = name;
        this.list = new ArrayList<>();
    }

    private List<MakeFood> list;
    
    public void receive(MakeFood makeFood) {
        list.add(makeFood);
        
    }
    
    public void assign() {
        System.out.println("waiter " + name + "分配制作任务");
        for (int i=0;i<list.size();i++) {
            list.get(i).execute();
        }
    }
}

  顾客类

package io.powerx.command;

public class Customer {

    private String name;
    public Customer(String name) {
        super();
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void orderBread(Waiter w,Cook c) {
        System.out.println(name+"order bread");
        w.receive(new MakeBread(c));
    }
    public void orderRoast(Waiter w,Cook c) {
        System.out.println(name+"order roast");
        w.receive(new MakeRoast(c));
    }
    public void orderChicken(Waiter w,Cook c) {
        System.out.println(name+"order chicken");
        w.receive(new MakeChicken(c));
    }
}

  执行测试代码,结果如下:

技术分享图片

    以上便是命令模式的标准实现,然而并没有达到顾客与厨师的解耦,我们的customer仍依赖于cook类,这tm就尴尬了,还不如直接在customer类中order*方法直接调cook的对应方法呢。只是学习设计模式切莫死搬硬套,我们只需要在waiter类中维护一个cook的list,由waiter决定哪个厨师来处理任务即可,这样我们的customer类就彻底与cook类解耦。

  修改waiter类如下

package io.powerx.command;

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

public class Waiter2 {
    
    private String name;
    
    private int currentIndex ;
    
    private List<Cook> cooklist;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<MakeFood> getList() {
        return list;
    }

    public void setList(List<MakeFood> list) {
        this.list = list;
    }

    
    public Waiter2(String name, Cook ...cooks) {
        super();
        this.name = name;
        this.cooklist = Arrays.asList(cooks);
        this.list = new ArrayList<>();
    }
    private List<MakeFood> list;
    
    public void receive(MakeFood makeFood) {
        list.add(makeFood);
        
    }
    
    public void assign() {
        System.out.println("waiter " + name + "分配制作任务");
        for (int i=0;i<list.size();i++) {
            list.get(i).execute();
        }
    }
    
    public Cook chooseCook() {
        Cook cook =  cooklist.get(currentIndex % cooklist.size()) ;
        currentIndex++;
        return cook;
    }

    public int getCurrentIndex() {
        return currentIndex;
    }

    public void setCurrentIndex(int currentIndex) {
        this.currentIndex = currentIndex;
    }

    public List<Cook> getCooklist() {
        return cooklist;
    }

    public void setCooklist(List<Cook> cooklist) {
        this.cooklist = cooklist;
    }
    
}

  同时顾客类的调用方式也发生了变化

package io.powerx.command;

public class Customer2 {

    private String name;
    public Customer2(String name) {
        super();
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void orderBread(Waiter2 w) {
        System.out.println(name+"order bread");
        w.receive(new MakeBread(w.chooseCook()));
    }
    public void orderRoast(Waiter2 w) {
        System.out.println(name+"order roast");
        w.receive(new MakeRoast(w.chooseCook()));
    }
    public void orderChicken(Waiter2 w) {
        System.out.println(name+"order chicken");
        w.receive(new MakeChicken(w.chooseCook()));
    }
}

  执行测试代码结果如下:

技术分享图片

  改造后的类图如下:

技术分享图片

  最后讲一下命令模式的适用场景,如果有以下需求,可以考虑使用命令模式:

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

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

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

  4、希望可以将请求组合使用。

以上是关于命令模式的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——git命令操作一个完整流程

用于从 cloudkit 检索单列的代码模式/片段

VSCode自定义代码片段——cli的终端命令大全

VSCode自定义代码片段4——cli的终端命令大全

VSCode自定义代码片段15——git命令操作一个完整流程

VSCode自定义代码片段15——git命令操作一个完整流程