目錄
- 命令模式
- 命令模式結構
- 命令模式適用場景
- 命令模式優缺點
- 練手題目
- 題目描述
- 輸入描述
- 輸出描述
- 題解
命令模式
命令模式是一種行為設計模式, 它可將請求轉換為一個包含與請求相關的所有信息的獨立對象。 該轉換讓你能根據不同的請求將方法參數化、 延遲請求執行或將其放入隊列中, 且能實現可撤銷操作。
命令模式結構
- 發送者(Sender)——亦稱 “觸發者(Invoker)”——類負責對請求進行初始化,其中必須包含一個成員變量來存儲對于命令對象的引用。發送者觸發命令,而不向接收者直接發送請求。注意,發送者并不負責創建命令對象:它通常會通過構造函數從客戶端處獲得預先生成的命令。
- 命令(Command)接口通常僅聲明一個執行命令的方法。
- 具體命令 (Concrete Commands)會實現各種類型的請求。具體命令自身并不完成工作,而是會將調用委派給一個業務邏輯對象。但為了簡化代碼,這些類可以進行合并。接收對象執行方法所需的參數可以聲明為具體命令的成員變量。你可以將命令對象設為不可變,僅允許通過構造函數對這些成員變量進行初始化。
- 接收者(Receiver)類包含部分業務邏輯。幾乎任何對象都可以作為接收者。絕大部分命令只處理如何將請求傳遞到接收者的細節,接收者自己會完成實際的工作。
- 客戶端(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();}
}
命令模式適用場景
-
如果你需要通過操作來參數化對象,可使用命令模式。
命令模式可將特定的方法調用轉化為獨立對象。 這一改變也帶來了許多有趣的應用: 你可以將命令作為方法的參數進行傳遞、 將命令保存在其他對象中, 或者在運行時切換已連接的命令等。
-
如果你想要將操作放入隊列中、操作的執行或者遠程執行操作,可使用命令模式。
同其他對象一樣,命令也可以實現序列化(序列化的意思是轉化為字符串),從而能方便地寫入文件或數據庫中。一段時間后,該字符串可被恢復成為最初的命令對象。因此,你可以延遲或計劃命令的執行。但其功能遠不止如此!使用同樣的方式,你還可以將命令放入隊列、記錄命令或者通過網絡發送命令。
-
如果你想要實現操作回滾功能,可使用命令模式。
**識別方法:**命令模式可以通過抽象或接口類型(發送者)中的行為方法來識別, 該類型調用另一個不同的抽象或接口類型 (接收者)實現中的方法,該實現則是在創建時由命令模式的實現封裝。命令類通常僅限于一些特殊行為。
命令模式優缺點
命令模式優點:
- 單一職責原則。你可以解耦觸發和執行操作的類。
- 開閉原則。你可以在不修改已有客戶端代碼的情況下在程序中創建新的命令。
- 你可以實現撤銷和恢復功能。
- 你可以實現操作的延遲執行。
- 你可以將一組簡單命令組合成一個復雜命令。
命令模式缺點:
- 代碼可能會變得更加復雜,因為你在發送者和接收者之間增加了一個全新的層次。
練手題目
題目描述
小明去奶茶店買奶茶,他可以通過在自助點餐機上來點不同的飲品,請你使用命令模式設計一個程序,模擬這個自助點餐系統的功能。
輸入描述
第一行是一個整數 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 {@Overridepublic void make() {System.out.println("MilkTea is ready!");}
}class Coffee extends Beverage {@Overridepublic void make() {System.out.println("Coffee is ready!");}
}class Cola extends Beverage {@Overridepublic 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());}@Overridepublic void execute() {beverage.make();}
}class CoffeeCommand extends Command {public CoffeeCommand() {super(new Coffee());}@Overridepublic void execute() {beverage.make();}
}class ColaCommand extends Command {public ColaCommand() {super(new Cola());}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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();}
}