Java设计模式-命令模式

简介: 命令(Command)模式是指将请求封装成为一个对象,使发出请求和执行请求的责任分割开,方便将命令对象进行存储、传递、调用、增加与管理。也就是将发送者、接收者和调用命令封装成独立的对象,来供客户端调用。属于行为模式的一种。

命令(Command)模式是指将请求封装成为一个对象,使发出请求和执行请求的责任分割开,方便将命令对象进行存储、传递、调用、增加与管理。

也就是将发送者、接收者和调用命令封装成独立的对象,来供客户端调用。属于行为模式的一种。

一、命令模式介绍

命令模式将发送者与接受者完全解耦,发送者与接收者之间没有直接的联系,发送者只需要如何发送请求,而不需要关心请求是如何完成的。下面就来看看命令模式的结构和实现:

1.1 命令模式的结构

将调用者和实现者进行分离,其结构如下所示:

image-20220405200443779

  • Command:抽象命令角色,声明执行命令的接口
  • Command1、Command2:具体命令角色,是抽象命令角色的具体实现类
  • ReceiverA、ReceiverB:具体实现,具体命令对象的真正实现者
  • Invoker:调用者,处理命令、实现命令的具体操作者,负责对外提供命令服务
  • Client:客户端

1.2 命令模式的实现

根据上面的结构图,可以实现如下代码:

/**
 * @description: 抽象命令类
 * @author: wjw
 * @date: 2022/4/5
 */
public interface Command {
   
   

    public abstract void execute();
}
/**
 * @description: 命令具体实现类1
 * @author: wjw
 * @date: 2022/4/5
 */
public class Command1 implements Command{
   
   

    private ReceiverA receiverA = new ReceiverA();

    @Override
    public void execute() {
   
   
        receiverA.action();
    }
}
/**
 * @description: 命令具体实现类2
 * @author: wjw
 * @date: 2022/4/5
 */
public class Command1 implements Command{
   
   

    private ReceiverA receiverA = new ReceiverA();

    @Override
    public void execute() {
   
   
        receiverA.action();
    }
}
/**
 * @description: 接收者类A
 * @author: wjw
 * @date: 2022/4/5
 */
public class ReceiverA {
   
   

    public void action() {
   
   
        System.out.println("我是ReceiverA");
    }
}
/**
 * @description: 具体实现者
 * @author: wjw
 * @date: 2022/4/5
 */
public class ReceiverB {
   
   

    public void action() {
   
   
        System.out.println("我是ReceiverB");
    }
}
/**
 * @description: 命令调用者
 * @author: wjw
 * @date: 2022/4/5
 */
public class Invoker {
   
   

    private Command command;

    public Invoker(Command command) {
   
   
        this.command = command;
    }

    public void setCommand(Command command) {
   
   
        this.command = command;
    }

    public void call() {
   
   
        System.out.println("调用者执行命令command");
        command.execute();
    }
}
/**
 * @description: 客户端
 * @author: wjw
 * @date: 2022/4/5
 */
public class Client {
   
   

    public static void main(String[] args) {
   
   
        Command command1 = new Command1();
        Invoker invoker1 = new Invoker(command1);
        invoker1.call();
    }
}

最后的客户端运行结果为:

调用者执行命令command
我是ReceiverA

下面来看看命令模式的应用场景

二、命令模式的应用场景

2.1 Spring 框架中的 JdbcTemplate

本文选取的Spring版本是5.3.1,来看看JdbcTemplate类中的query()方法:

image-20220405165646955

我们看到,上面的query()方法中定义了一个内部类QueryStatementCallback,并实现了StatementCallback接口,点开查看详细内容:

@FunctionalInterface
public interface StatementCallback<T> {
   
   
    //唯一的抽象方法
    @Nullable
    T doInStatement(Statement var1) throws SQLException, DataAccessException;
}

回到query()方法中,我们发现最后返回的execute(new QueryStatementCallback())中是将内部类QueryStatementCallback当做参数进行返回。这里QueryStatementCallback就相当于命令模式中的具体命令对象,而StatementCallback则是抽象命令对象。比如还有其他具体命令实现类,比如BatchUpdateStatementCallbackExecuteStatementCallback等等:

image-20220405174432889

看看execute()方法,为了方便理解,代码做了精简:

@Nullable
private <T> T execute(StatementCallback<T> action, boolean closeResources) throws DataAccessException {
   
   
    Assert.notNull(action, "Callback object must not be null");
    Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
    Statement stmt = null;

    Object var12;
    try {
   
   
        stmt = con.createStatement();
        this.applyStatementSettings(stmt);
        //执行doInStatement方法
        T result = action.doInStatement(stmt);
        this.handleWarnings(stmt);
        //赋值为var12
        var12 = result;
    } catch (SQLException var10) {
   
   
       //...
    } finally {
   
   
      //...
    }
    //最后返回statementCallback对象
    return var12;
}

根据上面的代码,可以梳理整个执行流程:

image-20220405195756378

实际上JdbcTemplate这个类是调用者(Invoker)、实现者(Receiver)和具体命令实现(Concrete Command)的集合, statementCallback则是命令的抽象接口。

三、命令模式实战

模拟在餐厅中点餐交给初始烹饪的场景,在该场景中点餐人员只需要把需要点的各种菜系交给服务员,服务员再把各项菜品交给厨师进行烹饪。如下图所示:

image-20220405205119777

我们先分析一下,命令是菜品具体实现是菜系,命令实现是厨师,调用者是服务员。所以该场景下的命令模式结构应该为:

image-20220405205915202

代码目录结构为:

├─src
│  ├─main
│  │  ├─java
│  │  │  └─cn
│  │  │      └─ethan
│  │  │          └─design
│  │  │              └─command
│  │  │                  │  Waiter.java
│  │  │                  │
│  │  │                  ├─cook
│  │  │                  │  │  ICook.java
│  │  │                  │  │
│  │  │                  │  └─impl
│  │  │                  │          GuangDongCook.java
│  │  │                  │          JiangSuCook.java
│  │  │                  │          ShanDongCook.java
│  │  │                  │          SiChuangCook.java
│  │  │                  │
│  │  │                  └─cuisine
│  │  │                      │  ICuisine.java
│  │  │                      │
│  │  │                      └─impl
│  │  │                              GuangDongCuisine.java
│  │  │                              JiangSuCuisine.java
│  │  │                              ShanDongCuisine.java
│  │  │                              SiChuangCuisine.java
│  │  │
│  │  └─resources
│  └─test
│      └─java
│          └─cn
│              └─ethan
│                  └─disign
│                          ApiTest.java

具体代码如下:

  1. 抽象命令者及其具体实现
/**
 * @description: 抽象命令接口(八大菜系)
 * @author: wjw
 * @date: 2022/4/5
 */
public interface ICuisine {
   
   

    /**烹调公共接口*/
    void cook();
}
/**
 * @description: 具体命令实现(广东菜)
 * @author: wjw
 * @date: 2022/4/5
 */
public class GuangDongCuisine implements ICuisine {
   
   

    private ICook cook;

    public GuangDongCuisine(ICook cook) {
   
   
        this.cook = cook;
    }

    @Override
    public void cook() {
   
   
        cook.doCooking();
    }
}
/**
 * @description: 命令具体实现(江苏菜)
 * @author: wjw
 * @date: 2022/4/5
 */
public class JiangSuCuisine implements ICuisine {
   
   

    private ICook cook;

    public JiangSuCuisine(ICook cook) {
   
   
        this.cook = cook;
    }

    @Override
    public void cook() {
   
   
        cook.doCooking();
    }
}
/**
 * @description: 具体命令实现(山东菜)
 * @author: wjw
 * @date: 2022/4/5
 */
public class ShanDongCuisine implements ICuisine {
   
   

    private ICook cook;

    public ShanDongCuisine(ICook cook) {
   
   
        this.cook = cook;
    }

    @Override
    public void cook() {
   
   
        cook.doCooking();
    }
}
/**
 * @description: 具体命令实现(四川菜)
 * @author: wjw
 * @date: 2022/4/5
 */
public class SiChuangCuisine implements ICuisine {
   
   

    private ICook cook;

    public SiChuangCuisine(ICook cook) {
   
   
        this.cook = cook;
    }

    @Override
    public void cook() {
   
   
        cook.doCooking();
    }
}
  1. 抽象实现者及其具体实现
/**
 * @description: 抽象实现者接口
 * @author: wjw
 * @date: 2022/4/5
 */
public interface ICook {
   
   

    /**厨师烹调*/
    void doCooking();
}
/**
 * @description: 具体实现者(广东厨师)
 * @author: wjw
 * @date: 2022/4/5
 */
public class GuangDongCook implements ICook {
   
   

    private Logger logger = LoggerFactory.getLogger(GuangDongCook.class);

    @Override
    public void doCooking() {
   
   
        logger.info("广东厨师,会做广东菜");
    }
}
/**
 * @description: 具体实现类(江苏厨师)
 * @author: wjw
 * @date: 2022/4/5
 */
public class JiangSuCook implements ICook {
   
   

    private Logger logger = LoggerFactory.getLogger(JiangSuCook.class);

    @Override
    public void doCooking() {
   
   
        logger.info("江苏厨师,会烧江苏菜");
    }
}
/**
 * @description: 具体实现类(山东厨师)
 * @author: wjw
 * @date: 2022/4/5
 */
public class ShanDongCook implements ICook {
   
   

    private Logger logger = LoggerFactory.getLogger(ShanDongCook.class);

    @Override
    public void doCooking() {
   
   
        logger.info("山东厨师会烧山东菜");
    }
}
/**
 * @description: 具体实现类(四川厨师)
 * @author: wjw
 * @date: 2022/4/5
 */
public class SiChuangCook implements ICook {
   
   

    private Logger logger = LoggerFactory.getLogger(SiChuangCook.class);

    @Override
    public void doCooking() {
   
   
        logger.info("四川厨师会烧四川菜");
    }
}
  1. 调用者及客户端
/**
 * @description: 调用者(服务员)
 * @author: wjw
 * @date: 2022/4/5
 */
public class Waiter {
   
   

    private Logger logger = LoggerFactory.getLogger(Waiter.class);

    private List<ICuisine> cuisineList = new ArrayList<>();

    public void order(ICuisine cuisine) {
   
   
        cuisineList.add(cuisine);
    }

    public synchronized void placeOrder() {
   
   
        for (ICuisine cuisine : cuisineList) {
   
   
            cuisine.cook();
        }
        cuisineList.clear();
    }
}
/**
 * @description: 客户端
 * @author: wjw
 * @date: 2022/4/5
 */
public class ApiTest {
   
   

    @Test
    public void test_command() {
   
   
        //菜和厨师命令实现
        ICuisine guangDongCuisine = new GuangDongCuisine(new GuangDongCook());
        ICuisine shanDongCuisine = new ShanDongCuisine(new ShanDongCook());
        ICuisine siChuangCuisine = new SiChuangCuisine(new SiChuangCook());
        ICuisine jiangSuCuisine = new JiangSuCuisine(new JiangSuCook());

        //调用者进行点单
        Waiter waiter = new Waiter();
        waiter.order(guangDongCuisine);
        waiter.order(shanDongCuisine);
        waiter.order(siChuangCuisine);
        waiter.order(jiangSuCuisine);

        //下单操作
        waiter.placeOrder();

    }
}

最终测试结果如下:

23:16:40.512 [main] INFO  c.e.d.c.cook.impl.GuangDongCook - 广东厨师,会做广东菜
23:16:40.518 [main] INFO  c.e.d.command.cook.impl.ShanDongCook - 山东厨师会烧山东菜
23:16:40.518 [main] INFO  c.e.d.command.cook.impl.SiChuangCook - 四川厨师会烧四川菜
23:16:40.518 [main] INFO  c.e.d.command.cook.impl.JiangSuCook - 江苏厨师,会烧江苏菜

参考资料

《重学Java设计模式》

http://c.biancheng.net/view/1380.html

目录
相关文章
|
15天前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
|
2天前
|
设计模式 Java
【JAVA基础篇教学】第十四篇:Java中设计模式
【JAVA基础篇教学】第十四篇:Java中设计模式
|
4天前
|
设计模式 算法 Java
设计模式在Java开发中的应用
设计模式在Java开发中的应用
15 0
|
10天前
|
设计模式 前端开发 Java
19:Web开发模式与MVC设计模式-Java Web
19:Web开发模式与MVC设计模式-Java Web
20 4
|
10天前
|
设计模式 存储 前端开发
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
25 4
|
11天前
|
设计模式 缓存 监控
JAVA设计模式之结构型模式
结构模型:适配器模型、桥接模型、过滤器模型、组合模型、装饰器模型、外观模型、享受元模型和代理模型。
21 3
|
14天前
|
设计模式 算法 Java
Java基础教程(19)-设计模式简述
【4月更文挑战第19天】设计模式是软件设计中反复使用的代码设计经验,旨在提升代码的可重用性、可扩展性和可维护性。23种模式分为创建型、结构型和行为型三类。创建型模式如工厂方法、抽象工厂、建造者、原型和单例,关注对象创建与使用的分离。结构型模式涉及对象组合,如适配器、装饰器、外观等,增强结构灵活性。行为型模式专注于对象间职责分配和算法合作,包括责任链、命令、观察者等。设计模式提供标准化解决方案,促进代码交流和复用。
|
15天前
|
设计模式 Java
Java 设计模式:混合、装饰器与组合的编程实践
【4月更文挑战第27天】在面向对象编程中,混合(Mixins)、装饰器(Decorators)和组合(Composition)是三种强大的设计模式,用于增强和扩展类的功能。
21 1
|
15天前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
35 2
|
15天前
|
设计模式 算法 Java
Java 设计模式:深入模板方法模式的原理与应用
【4月更文挑战第27天】模板方法模式是一种行为设计模式,主要用于定义一个操作中的算法的框架,允许子类在不改变算法结构的情况下重定义算法的某些特定步骤。
22 1