生活場景: 普通用戶電腦開機問題,客戶端發出命令或請求,不關心真正的接收者是誰,也不關心具體如何實現,而且同一個請求的動作可以有不同的請求內容,當然具體的處理功能也不一樣。
同理: 老闆(Boss)給項目經理(Leader)下達任務,項目經理會根據實際情況給每個員工派發任務,待員工把任務完成後,再由項目經理向老闆彙報結果。
概念
將一個請求封裝成一個對象,從而使你可用不同的請求對客戶進行參數化,對請求排隊或記錄請求日誌,以及支持可撤銷的操作。將”發出請求的對象”和”接收與執行這些請求的對象”分隔開來。
參與者
序號 | 參與者 | 說明 |
---|---|---|
1 | Command | 聲明執行操作的接口 |
2 | ConcreteCommand | 將一個接收者對象綁定於一個動作, 調用接收者相應的操作,以實現Execute |
3 | Client | 創建一個具體命令對象並設定它的接收者。 |
4 | Invoker | 要求該命令執行這個請求。 |
5 | Receiver | 知道如何實現與執行一個請求相關的操作。任何類都可能作爲一個接收者。 |
舉個栗子:
Client: 看電視的人
Invoker: 遙控器
Command: 電信號
ConcreteCommand: 具體命令,遙控器上的按鍵對應的不同的電信號
Receiver: 電視機
相關的類圖
案例一
模擬電腦開機的案例
package com.hanker.dao.impl;
//主板接口--抽象Receiver
interface MainBoardApi{
public void open();//開機功能
}
//技嘉主板--具體接收者Receiver
class GigaMainBoard implements MainBoardApi{
//真正的開機命令的實現
public void open() {
System.out.println("技嘉主板現在正在開機,請稍候...");
System.out.println("接通電源...");
System.out.println("設備檢查...");
System.out.println("裝載系統...");
System.out.println("機器已經正常打開,請操作...");
}
}
//微星主板--具體接收者Receiver
class MsiMainBoard implements MainBoardApi{
//真正的開機命令的實現
public void open() {
System.out.println("微星主板現在正在開機,請稍候...");
System.out.println("接通電源...");
System.out.println("設備檢查...");
System.out.println("裝載系統...");
System.out.println("機器已經正常打開,請操作...");
}
}
//命令接口
interface Command{
public void execute(); //執行命令
}
//開機命令--具體Command
class OpenCommand implements Command{
private MainBoardApi mainBoard;//持有真正的實現命令的接受者——主板對象
//構造方法傳入主板對象
public OpenCommand(MainBoardApi mainBoardApi) {
this.mainBoard = mainBoardApi;
}
@Override
public void execute() {
//對於命令對象,根本不知道如何開機,會轉調用主板對象
//讓主板對象去完成開機的功能
this.mainBoard.open();
}
}
//機箱對象--調用者Invoker
class Box{
private Command openCommand;//開機命令對象
//設置開機命令對象
public void setCommand(Command command) {
this.openCommand = command;
}
//提供給客戶使用,接收並響應用戶請求,相當於按鈕被按下觸發的方法
public void openButtonPressed() {
openCommand.execute();
}
}
public class CommandPatternTest2 {
//client
public static void main(String[] args) {
//1.把命令和真正的實現組合起來,相當於在組裝機器
//把機箱上按鈕的連接線插接到主板上
MainBoardApi mainBoard = new GigaMainBoard();
Command openCommand = new OpenCommand(mainBoard);
//2.爲機箱上的按鈕設置相應的命令,讓按鈕知道該幹什麼
Box box = new Box();
box.setCommand(openCommand);
//3.模擬按下機箱上的按鈕
box.openButtonPressed();
}
}
參數化配置
所謂命令模式的參數化配置,指的是:可以用不同的命令對象,去參數化配置客戶的請求。
下面增加一個命令重啓命令,實現參數化配置。在原來的代碼上增加方法:
package com.kongfanyu.springcloud.config;
//主板接口
interface MainBoardApi{
public void open();//開機功能
public void reset();//重啓功能
}
//技嘉主板
class GigaMainBoard implements MainBoardApi{
//真正的開機命令的實現
public void open() {
System.out.println("技嘉主板現在正在開機,請稍候...");
System.out.println("接通電源...");
System.out.println("設備檢查...");
System.out.println("裝載系統...");
System.out.println("機器已經正常打開,請操作...");
}
//真正重啓命令的實現
@Override
public void reset() {
System.out.println("技嘉主板現在正在重新啓動機器,請等候..");
System.out.println("機器已經正常打開,請操作");
}
}
//微星主板
class MsiMainBoard implements MainBoardApi{
//真正的開機命令的實現
public void open() {
System.out.println("微星主板現在正在開機,請稍候...");
System.out.println("接通電源...");
System.out.println("設備檢查...");
System.out.println("裝載系統...");
System.out.println("機器已經正常打開,請操作...");
}
//真正重啓命令的實現
@Override
public void reset() {
System.out.println("微星主板現在正在重新啓動機器,請等候..");
System.out.println("機器已經正常打開,請操作");
}
}
//命令接口
interface Command{
public void execute(); //執行命令
}
//開機命令
class OpenCommand implements Command{
private MainBoardApi mainBoard;//持有真正的實現命令的接受者——主板對象
//構造方法傳入主板對象
public OpenCommand(MainBoardApi mainBoardApi) {
this.mainBoard = mainBoardApi;
}
@Override
public void execute() {
//對於命令對象,根本不知道如何開機,會轉調用主板對象
//讓主板對象去完成開機的功能
this.mainBoard.open();
}
}
//重啓命令
class ResetCommand implements Command{
private MainBoardApi mainBoard;//持有真正的實現命令的接受者——主板對象
//構造方法傳入主板對象
public ResetCommand(MainBoardApi mainBoardApi) {
this.mainBoard = mainBoardApi;
}
@Override
public void execute() {
//對於命令對象,根本不知道如何重啓機器,會轉調用主板對象
//讓主板去完成重啓機器的功能
this.mainBoard.reset();
}
}
//機箱對象
class Box{
private Command openCommand;//開機命令對象
private Command resetCommand;//重啓機器命令對象
//設置開機命令對象
public void setCommand(Command command) {
this.openCommand = command;
}
//設置重啓命令對象
public void setResetCommand(Command command){
this.resetCommand = command;
}
//提供給客戶使用,接收並響應用戶請求,相當於按鈕被按下觸發的方法
public void openButtonPressed() {
openCommand.execute();
}
//提供給用戶使用,接收並響應用戶請求,相當於重啓按鈕被按下觸發的方法
public void resetButtonPressed(){
//按下按鈕,執行命令
this.resetCommand.execute();;
}
}
public class CommandPatternTest2 {
public static void main(String[] args) {
//1.把命令和真正的實現組合起來,相當於在組裝機器
//把機箱上按鈕的連接線插接到主板上
MainBoardApi mainBoard = new GigaMainBoard();
//創建開機命令
Command openCommand = new OpenCommand(mainBoard);
Command resetCommand = new ResetCommand(mainBoard);
//2.爲機箱上的按鈕設置相應的命令,讓按鈕知道該幹什麼
Box box = new Box();
box.setCommand(openCommand);
box.setResetCommand(resetCommand);
//3.模擬按下機箱上的按鈕
System.out.println("正確配置下=======================>");
System.out.println(">>>按下開機按鈕:>>>");
box.openButtonPressed();
System.out.println(">>>按下重啓按鈕:>>>");
box.resetButtonPressed();
}
}
執行結果:
正確配置下=======================>
>>>按下開機按鈕:>>>
技嘉主板現在正在開機,請稍候...
接通電源...
設備檢查...
裝載系統...
機器已經正常打開,請操作...
>>>按下重啓按鈕:>>>
技嘉主板現在正在重新啓動機器,請等候..
機器已經正常打開,請操作
Process finished with exit code 0
還有廚師做飯的案例也是命令模式的典型場景。
總結
命令模式的核心思想就是將命令或者請求封裝成對象,分離請求調用者和請求最終執行者。
優點:
將請求調用者和執行者解耦,適用於底層接口封裝,可以通過只增加類就可以實現接口擴展,不需要修改原來的代碼。
缺點:
如果存在較多的命令或者請求,需要較多的命令類。