命令模式

命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

命令模式结构

在这里插入图片描述

  1. 发送者(Sender)——亦称 “触发者(Invoker)”——类负责对请求进行初始化,其中必须包含一个成员变量来存储对于命令对象的引用。发送者触发命令,而不向接收者直接发送请求。注意,发送者并不负责创建命令对象:它通常会通过构造函数从客户端处获得预先生成的命令。
  2. 命令(Command)接口通常仅声明一个执行命令的方法。
  3. 具体命令 (Concrete Commands)会实现各种类型的请求。具体命令自身并不完成工作,而是会将调用委派给一个业务逻辑对象。但为了简化代码,这些类可以进行合并。接收对象执行方法所需的参数可以声明为具体命令的成员变量。你可以将命令对象设为不可变,仅允许通过构造函数对这些成员变量进行初始化。
  4. 接收者(Receiver)类包含部分业务逻辑。几乎任何对象都可以作为接收者。绝大部分命令只处理如何将请求传递到接收者的细节,接收者自己会完成实际的工作。
  5. 客户端(Client)会创建并配置具体命令对象。客户端必须将包括接收者实体在内的所有请求参数传递给命令的构造函数。此后,生成的命令就可以与一个或多个发送者相关联了。

命令模式通用代码:

//抽象接收者
public abstract class Receiver{
	public abstract void operation();
}

//具体接收者
public class Recevier1 extends Recevier{
	public void operation(){
		...
	}
}

//通用命令接口
public interface Command{
	void execute();
}

//具体命令类
public class ConcreteCommand1 implements Command{
	private Receiver receiver;
	
	public ConcreteCommand1(Receiver _receiver){
		 this.receiver = _receiver;
	}
	
	public void execute(){
		this.receiver.operation();
	}
}

//调用者类
public class Invoker{
	private Command command;
	
	public void setCommand(Command _command){
		this.command = _command;
	}
	
	public void executeCommand(){
		this.command.execute();
	}
	
}

//主程序类
public class Client{
	public static void main(String[] args){
		//调用者
		Invoker invoker = new Invoker();
		//接收者
		Receiver receiver1 = new Receiver1();
		//定义一个命令
		Command command = new ConcreteCommand1(receiver1);
		
		invoker.setCommand(command);
		invoker.executeCommand();
	}
}

命令模式适用场景

  1. 如果你需要通过操作来参数化对象,可使用命令模式。

    命令模式可将特定的方法调用转化为独立对象。 这一改变也带来了许多有趣的应用: 你可以将命令作为方法的参数进行传递、 将命令保存在其他对象中, 或者在运行时切换已连接的命令等。

  2. 如果你想要将操作放入队列中、操作的执行或者远程执行操作,可使用命令模式。

    同其他对象一样,命令也可以实现序列化(序列化的意思是转化为字符串),从而能方便地写入文件或数据库中。一段时间后,该字符串可被恢复成为最初的命令对象。因此,你可以延迟或计划命令的执行。但其功能远不止如此!使用同样的方式,你还可以将命令放入队列、记录命令或者通过网络发送命令。

  3. 如果你想要实现操作回滚功能,可使用命令模式。

在这里插入图片描述

**识别方法:**命令模式可以通过抽象或接口类型(发送者)中的行为方法来识别, 该类型调用另一个不同的抽象或接口类型 (接收者)实现中的方法,该实现则是在创建时由命令模式的实现封装。命令类通常仅限于一些特殊行为。

命令模式优缺点

命令模式优点:

  • 单一职责原则。你可以解耦触发和执行操作的类。
  • 开闭原则。你可以在不修改已有客户端代码的情况下在程序中创建新的命令。
  • 你可以实现撤销和恢复功能。
  • 你可以实现操作的延迟执行。
  • 你可以将一组简单命令组合成一个复杂命令。

命令模式缺点:

  • 代码可能会变得更加复杂,因为你在发送者和接收者之间增加了一个全新的层次。

练手题目

题目描述

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

输入描述

第一行是一个整数 n(1 ≤ n ≤ 100),表示点单的数量。

接下来的 n 行,每行包含一个字符串,表示点餐的饮品名称。

输出描述

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

在这里插入图片描述

题解

解法一:

import java.util.Scanner;

// 枚举类表示饮料类型
enum BeverageType {
    MILKTEA, COFFEE, COLA
}

// 抽象饮料类
abstract class Beverage {
    abstract void make();
}

// 具体饮料类
class MilkTea extends Beverage {
    @Override
    public void make() {
        System.out.println("MilkTea is ready!");
    }
}

class Coffee extends Beverage {
    @Override
    public void make() {
        System.out.println("Coffee is ready!");
    }
}

class Cola extends Beverage {
    @Override
    public void make() {
        System.out.println("Cola is ready!");
    }
}

// 抽象命令类
abstract class Command {
    protected Beverage beverage;

    public Command(Beverage _beverage) {
        this.beverage = _beverage;
    }

    abstract void execute();
}

// 具体命令类
class MilkTeaCommand extends Command {
    public MilkTeaCommand() {
        super(new MilkTea());
    }

    @Override
    public void execute() {
        beverage.make();
    }
}

class CoffeeCommand extends Command {
    public CoffeeCommand() {
        super(new Coffee());
    }

    @Override
    public void execute() {
        beverage.make();
    }
}

class ColaCommand extends Command {
    public ColaCommand() {
        super(new Cola());
    }

    @Override
    public void execute() {
        beverage.make();
    }
}

// 调用者类
class Invoker {
    private Command command;

    public void setCommand(Command _command) {
        command = _command;
    }

    public void action() {
        command.execute();
    }
}

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

        try {
            Invoker invoker = new Invoker();

            int n = scanner.nextInt();
            scanner.nextLine();

            for (int i = 0; i < n; i++) {
                String type = scanner.nextLine().trim().toUpperCase();

                try {
                    switch (BeverageType.valueOf(type)) {
                        case MILKTEA:
                            invoker.setCommand(new MilkTeaCommand());
                            break;
                        case COFFEE:
                            invoker.setCommand(new CoffeeCommand());
                            break;
                        case COLA:
                            invoker.setCommand(new ColaCommand());
                            break;
                        default:
                            System.out.println("请重新输入:");
                            continue;
                    }
                    invoker.action();
                } catch (IllegalArgumentException e) {
                    System.out.println("无效的饮料类型");
                }
            }

        } catch (Exception e) {
            System.out.println("发生错误: " + e.getMessage());
        } finally {
            scanner.close();
        }
    }
}

解法二:

import java.util.Scanner;

// 命令接口
interface Command {
    void execute();
}

// 具体命令类 - 点餐命令
class OrderCommand implements Command {
    private String drinkName;
    private DrinkMaker receiver;

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

    @Override
    public void execute() {
        receiver.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();
    }
}

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

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

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

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

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

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

解法三:命令模式+工厂模式

import java.util.Scanner;

// 命令接口
interface Command {
    void execute();
}

// 具体命令类 - 点餐命令
class OrderCommand implements Command {
    private String drinkName;
    private DrinkMaker receiver;

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

    @Override
    public void execute() {
        receiver.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() {
        if (command != null) {
            command.execute();
        } else {
            System.out.println("未设置命令.");
        }
    }
}

// 命令工厂类
class CommandFactory {
    private DrinkMaker drinkMaker;

    public CommandFactory(DrinkMaker drinkMaker) {
        this.drinkMaker = drinkMaker;
    }

    public Command createCommand(String drinkName) {
        return new OrderCommand(drinkName, drinkMaker);
    }
}

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

        // 创建接收者和工厂对象
        DrinkMaker drinkMaker = new DrinkMaker();
        CommandFactory commandFactory = new CommandFactory(drinkMaker);
        OrderMachine orderMachine = new OrderMachine();

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

        while (n-- > 0) {
            // 读取命令
            String drinkName = scanner.nextLine().trim();

            if (drinkName.isEmpty()) {
                System.out.println("无效输入,请输入饮品名.");
                continue;
            }

            // 使用工厂创建命令对象
            Command command = commandFactory.createCommand(drinkName);

            // 设置命令并执行
            orderMachine.setCommand(command);
            orderMachine.executeOrder();
        }
        scanner.close();
    }
}
Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐