笨蛋学设计模式行为型模式-命令模式【19】

发布时间:2024年01月22日

8.6命令模式

8.6.1概念

? 命令模式允许将请求封装成一个对象(命令对象,包含执行操作所需地所有信息),并将命令对象按照一定的顺序存储在队列中,然后再逐一调用执行,这些命令可以支持反向操作,进行撤销和重做。这样发送者就只需要触发命令就可以完成操作,不需要知道接受者的具体操作,从而实现两者间的解耦

8.6.2场景

? 在餐饮业中,命令模式可以用于实现订单的处理流程。例如在餐厅中,当我们在客户端或者小程序上进行点单,客户端 / 小程序会将我们在上面下的订单请求封装成为一个对象,通过发送命令给后厨的工作人员上,而他们就会根据我们下的具体订单进行菜品备菜、菜品制作、菜品上菜等一系列操作。

8.6.3优势 / 劣势

  • 降低系统的耦合度:将请求的发送者和接收者解耦
  • 提高系统扩展性:可以方便地扩展新的命令和接收者,提高系统的扩展力
  • 增加请求队列:可以很容易地增加一个请求队列,以便在需要时处理或延迟处理请求
  • 支持撤销操作:通过记录一系列命令,可以很轻松地回溯并撤销之前地操作

  • 增加系统的复杂性:需要创建多个类和对象
  • 执行顺序不保证:不能保证命令的执行顺序

8.6.4命令模式可分为

  • 命令接口Command:接口或抽象类,定义执行操作的接口
  • 具体命令类ConcreteCommand:实现命令接口,执行具体操作,在调用execute方法时"接收者对象"根据命令完成具体的任务,比如遥控器中的"开机","关机"命令
  • 接收者类Receiver:接受并执行命令的对象,可以是任何对象,遥控器可以控制空调,也可以控制电视机,电视机和空调负责执行具体操作,是接收者
  • 调用者类Invoker:发起请求的对象,有一个将命令作为参数传递的方法。它不关心命令的具体实现,只负责调用命令对象的execute()方法来传递请求,在本例中,控制遥控器的"人"就是调用者
  • 客户端:创建具体的命令对象和接收者对象,然后将它们组装起来

8.6.5命令模式

package com.technologystatck.designpattern.mode.command;

import java.util.*;

public class Commands {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        ConcreteCommand command = new ConcreteCommand(receiver);
        new Invoker(command).executeCommand();
    }
}
//定义执行操作的接口,包含一个execute方法,有时还会包括unExecute方法,表示撤销命令
interface Command{
    //执行方法
    void execute();
    //撤销方法
    void undo();
}


//实现命令接口,执行具体的操作
class ConcreteCommand implements Command{
    //接收者对象
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

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

    @Override
    public void undo() {
        receiver.unAction();
    }
}

//定义接受者类,知道如何实施与执行一个请求相关的操作
class Receiver{
    public void action(){
        //执行操作
    }
    public void unAction(){
        //撤销操作
    }
}
//定义调用者类,调用命令对象执行请求
class Invoker{
    private Command command;

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

    public void executeCommand(){
        command.execute();
    }
}
//调用者类可以维护一个命令队列或者撤销栈,以支持批处理和撤销命令
class Invoker{
    //命令队列
    private Queue<Command> commandQueue;
    //撤销栈
    private Stack<Command> undoStack;


    public Invoker(Queue<Command> commandQueue, Stack<Command> undoStack) {
        this.commandQueue = new LinkedList<>();
        this.undoStack = new Stack<>();
    }

    //设置命令并执行
    public void setAndExecuteCommand(Command command){
        command.execute();
        commandQueue.offer(command);
        undoStack.push(command);
    }

    //撤销上一个命令
    public void undoLastCommand(){
        if(!undoStack.isEmpty()){
            Command lastCommand = undoStack.pop();
            //命令类实现undo方法
            lastCommand.undo();
            commandQueue.remove(lastCommand);
        }else{
            System.out.println("No command to undo.");
        }
    }

    //执行命令队列中的所有命令
    public void executeCommandsInQueue(){
        for (Command command : commandQueue) {
            command.execute();
        }
    }
}

8.6.6实战

8.6.6.1题目描述

小明去奶茶店买奶茶,他可以通过在自助点餐机上来点不同的饮品,请你使用命令模式设计一个程序,模拟这个自助点餐系统的功能。

8.6.6.2输入描述
  • 第一行是一个整数 n(1 ≤ n ≤ 100),表示点单的数量。
  • 接下来的 n 行,每行包含一个字符串,表示点餐的饮品名称。
8.6.6.3输出描述

输出执行完所有点单后的制作情况,每行输出一种饮品的制作情况。如果制作完成,输出 “XXX is ready!”,其中 XXX 表示饮品名称。

8.6.6.4代码
package com.technologystatck.designpattern.mode.command;

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        //创建接收者和命令对象
        DrinkMaker drinkMaker = new DrinkMaker();

        //读取命令数量
        int nums=scanner.nextInt();
        scanner.nextLine();

        while(nums-- > 0){
            //读取命令
            String drinkName=scanner.next();

            //创建命令对象
            OrderCommand command = new OrderCommand(drinkName, drinkMaker);

            //执行命令
            OrderMachine orderMachine = new OrderMachine();
            orderMachine.setCommand(command);
            orderMachine.executeOrder();
        }
        scanner.close();
    }
}

//定义执行操作的命令订单接口
interface Command{
    void execute();
}

//定义具体命令类实现点餐
class OrderCommand implements Command{
    //饮料名称
    private String drinkName;
    //具体的饮料制作者
    private DrinkMaker drinkMaker;

    public OrderCommand(String drinkName, DrinkMaker drinkMaker) {
        this.drinkName = drinkName;
        this.drinkMaker = drinkMaker;
    }

    @Override
    public void execute() {
        drinkMaker.makeDrink(drinkName);
    }
}

//接收者类-制作饮料
class DrinkMaker{
    public void makeDrink(String drinkName){
        System.out.println(drinkName+" is ready!");
    }
}

//调用者类-点餐机
class OrderMachine{
    private Command command;

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

    //执行下单方法
    public void executeOrder(){
        command.execute();
    }
}

8.6.7总结

  • 优点:方便扩展新的命令和接收者,还可以撤销操作,增加系统的灵活性和可维护性
  • 总结:将请求封装成一个对象,通过具体命令类、调用者、接收者以此来完成相关操作
  • 场景:适用于需要实现撤销操作,需要保证事务一致性等场景
文章来源:https://blog.csdn.net/YSL_Monet/article/details/135744197
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。