设计模式之命令模式

1. 命令模式概念

命令模式的定义是将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

在面向对象编程当中,一个很重要的事情是设计使得代码松耦合。命令模式在生活中的体现就是遥控器和电视,遥控器是命令的触发者会给电视这个命令接受者发送一条换台命令,电视才会去执行这个换台命令,遥控器和电视就是通过命令关联起来,当遥控器坏了,我们不必去直接更换电视这个接受者,我们只需要更换命令的触发者遥控器即可。

命令模式优点:

  • 通过引入中间件降低系统的耦合度。

  • 扩展性良好,采用命令模式增加、删除命令不会影响其他类,满足开闭原则。

命令模式缺点:

  • 可能产生大量具体的命令类,因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
  • 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构增加了理解上的困难。

命令模式类图如下:

在这里插入图片描述

  • 抽象命令类(Command):这是表示命令封装的抽象,它定义了命令的执行与取消行为,应该被所有的具体命令类实现。
  • 具体命令类(ConcreteCommand):具体的命令实现,它关联一个接收者,最终交由接收者进行处理。
  • 命令接收者(Receiver):负责执行命令相关的操作的角色。
  • 命令触发者(Invoker):触发命令。

2. 命令模式案例

客户去餐厅点单就餐,会通过服务员来下单,服务员根据菜单给后厨下发做菜的命令,比如下发甜点命令给甜点师,下发面点命令给面点师,具体实现如下:

2.1 抽象命令

新建抽象的命令接口定义命令执行和取消执行的操作,接口名称为Command,内容如下:

public interface Command {

    /**
     * 执行命令
     */
    void excute();

    /**
     * 撤销命令
     */
    void unExcute();
}

2.2 命令接收者

新建命令接收者甜点师类,甜点师负责如何制作甜点,类名称为DessertTeacherReceiver,内容如下:

public class DessertTeacherReceiver {

    public void action(){
        System.out.println("甜品师:开始制作甜品......");
    }

    public void cancel(){
        System.out.println("甜品师:取消制作甜品......");
    }
}

类似的新建命令接收者面点师类,面点师负责如何制作面点,类名称为PastryTeacherRecever,内容如下:

public class PastryTeacherRecever {

    public void action() {
        System.out.println("面点师:开始制作面条......");
    }

    public void cancel() {
        System.out.println("面点师:取消制作面条......");
    }
}

2.3 具体命令类

新建甜点命令类,它聚合了甜点师实现了抽象命令接口,类名称为DessertCommond,内容如下:

public class DessertCommond implements Command {

    /**
     * 甜品师
     */
    private DessertTeacherReceiver receiver;

    public DessertCommond(DessertTeacherReceiver receiver){
        this.receiver = receiver;
    }

    @Override
    public void excute() {
        receiver.action();
    }

    @Override
    public void unExcute() {
        receiver.cancel();
    }
}

类似的新建面点命令类,它聚合了面点师实现了抽象命令接口,类名称为PastryCommand,内容如下:

public class PastryCommand implements Command {

    /**
     * 面点师
     */
    private PastryTeacherRecever receiver;

    public PastryCommand (PastryTeacherRecever receiver){
        this.receiver = receiver;
    }

    @Override
    public void excute() {
        receiver.action();
    }

    @Override
    public void unExcute() {
        receiver.cancel();
    }
}

2.4 命令触发者

新建服务员命令触发者类,对客户端提供下单和撤单操作,内部维护一个菜单,类名称为ServiceInvoker,内容如下:

public class ServiceInvoker {

    /**
     * 定义一个菜单
     */
    private static Map<String, Command> menu;

    /**
     * 初始化菜单
     */
    static {
        menu = new HashMap<>(2);
        menu.put("甜品", new DessertCommond(new DessertTeacherReceiver()));
        menu.put("面点", new PastryCommand(new PastryTeacherRecever()));
    }

    /**
     * 下单
     */
    public void placeOrder(String foodName){
        // 发布命令
        menu.get(foodName).excute();
    }

    /**
     * 撤单
     */
    public void cancellations(String foodName){
        // 发布命令
        menu.get(foodName).unExcute();
    }
}

2.5 客户端

新建客户端进行点餐,内容如下:

public class Client {

    public static void main(String[] args) {
        // 创建一个服务员
        ServiceInvoker serviceInvoker = new ServiceInvoker();

        // 点一个甜品
        serviceInvoker.placeOrder("甜品");

        // 点一个面点
        serviceInvoker.placeOrder("面点");

        // 撤销甜品
        serviceInvoker.cancellations("甜品");
    }
}

在这里插入图片描述

3. 命令模式应用

在Spring的JDBC模块中有一个JdbcTemplate类就是用了类似的命令模式,其中JdbcTemplate的query()方法如下:

@Override
@Nullable
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
    Assert.notNull(sql, "SQL must not be null");
    Assert.notNull(rse, "ResultSetExtractor must not be null");
    if (logger.isDebugEnabled()) {
        logger.debug("Executing SQL query [" + sql + "]");
    }

    /**
		 * Callback to execute the query.
		 */
    class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
        @Override
        @Nullable
        public T doInStatement(Statement stmt) throws SQLException {
            ResultSet rs = null;
            try {
                rs = stmt.executeQuery(sql);
                return rse.extractData(rs);
            }
            finally {
                JdbcUtils.closeResultSet(rs);
            }
        }
        @Override
        public String getSql() {
            return sql;
        }
    }

    return execute(new QueryStatementCallback());
}

QueryStatementCallback就是一个实现了StatementCallback接口的一个具体命令,这个命令最终会交给Statement对象去具体执行,JdbcTemplate充当的就是命令触发者这样的角色。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值