設計模式—— 十五 :命令模式
@目錄
什麼是命令模式?
命令模式的定義:
Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.(將一個請求封裝成一個物件,從而讓你使用不同的請求把客戶端引數化,對請求排隊或者記錄請求日誌,可以提供命令的撤銷和恢復功能。) |
命令模式的核心在於引入了命令類,通過命令類來降低傳送者和接收者的耦合度,請求傳送 者只需指定一個命令物件,再通過命令物件來呼叫請求接收者的處理方法。
命令模式通用類圖如下:
命令模式包含這麼幾個角色:
- Command(抽象命令):宣告需要執行的命令
- ConcreteCommand(具體命令):實現宣告的命令
- Receive(接收者):接收者執行與請求相關的操作,它具體實現對請求的業務處理
- Invoker(呼叫者):呼叫者即請求傳送者,它通過命令物件來執行請求。
命令模式的本質是對請求進行封裝,一個請求對應於一個命令,將發出命令的責任和執行命令的責任分割開。每一個命令都是一個操作:請求的一方發出請求要求執行一個操作;接收的一方收到請求,並執行相應的操作。命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的介面,更不必知道請求如何被接收、操作是否被執行、何時被執行,以及是怎麼被執行的。
命令模式通用程式碼如下:
- Receiver:Receiver類實現具體的業務需求
/**
* @author 三分惡
* @date 2020年6月28日
* @description 接收者
*/
public class Receiver {
public void doSomething() {
//具體的業務邏輯
}
}
- 抽象的Command類:
/** * @author 三分惡 * @date 2020年6月28日 * @description 抽象的Command類 */ public abstract class Command { // 每個命令類都必須有一個執行命令的方法 public abstract void execute(); }
- 具體的Command類:根據需求,具體的命令類可以有多個:
/**
* @author 三分惡
* @date 2020年6月28日
* @description 具體的命令類
*/
public class ConcreteCommand extends Command {
// 維持一個對請求接收者物件的引用
private Receiver receiver;
//建構函式傳遞接收者
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
public void execute() {
// 呼叫請求接收者的業務處理方法doSomething()
receiver.doSomething();
}
}
- 呼叫者Invoker類:
/**
* @author 三分惡
* @date 2020年6月28日
* @description 呼叫者
*/
public class Invoker {
private Command command;
// 設值注入
public void setCommand(Command command) {
this.command = command;
}
// 執行命令
public void action() {
this.command.execute();
}
}
- 場景類:
/**
* @author 三分惡
* @date 2020年6月28日
* @description
*/
public class Client {
public static void main(String[] args) {
//首先宣告呼叫者Invoker
Invoker invoker = new Invoker();
//定義接收者
Receiver receiver = new Receiver();
//定義一個傳送給接收者的命令
Command command = new ConcreteCommand(receiver);
//把命令交給呼叫者去執行
invoker.setCommand(command);
invoker.action();
}
}
為什麼要用命令模式?
假設有一個這樣的業務場景:一個正在開發的軟體專案,分為三個組:需求組、美工組、開發組。在開發的過程中,客戶需要和廠商溝通,和需求組討論需求、和 美工討論頁面、和程式碼組討論實現,告訴他們修改、刪除、增加各種內容等。
使用命令模式前
上面的業務場景抽象成類圖:
根據類圖進行具體的編碼實現:
- Group:抽象類,定義業務:
/**
* @author 三分惡
* @date 2020年6月28日
* @description 抽象Group
*/
public abstract class Group {
// 甲乙雙方分開辦公,如果甲方要和某個組討論,首先要找到這個組
public abstract void find();
// 要求增加功能
public abstract void add();
// 要求刪除功能
public abstract void delete();
// 要求修改功能
public abstract void change();
// 要求給出所有的變更計劃
public abstract void plan();
}
- RequirementGroup(需求組):
public class RequirementGroup extends Group{
@Override
public void find() {
System.out.println("找到需求組...");
}
@Override
public void add() {
System.out.println("客戶要求增加一項需求...");
}
@Override
public void delete() {
System.out.println("客戶要求刪除一項需求...");
}
@Override
public void change() {
System.out.println("客戶要求修改一項需求...");
}
@Override
public void plan() {
System.out.println("客戶要求需求變更計劃...");
}
}
- PageGroup(美工組):
public class PageGroup extends Group{
@Override
public void find() {
System.out.println("找到美工組...");
}
@Override
public void add() {
System.out.println("客戶要求增加一個頁面...");
}
@Override
public void delete() {
System.out.println("客戶要求刪除一個頁面...");
}
@Override
public void change() {
System.out.println("客戶要求改變一個頁面...");
}
@Override
public void plan() {
System.out.println("客戶要求頁面變更計劃...");
}
}
- CodeGroup(開發組):
public class CodeGroup extends Group{
@Override
public void find() {
System.out.println("找到程式碼組...");
}
@Override
public void add() {
System.out.println("客戶要求增加一項功能...");
}
@Override
public void delete() {
System.out.println("客戶要求刪除一項功能...");
}
@Override
public void change() {
System.out.println("客戶要求修改一項功能...");
}
@Override
public void plan() {
System.out.println("客戶要求程式碼變更計劃...");
}
}
- 場景類:
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
//首先客戶找到需求組說,過來談需求,並修改
System.out.println("-----------客戶要求增加一項需求---------------");
Group rg = new RequirementGroup();
//找到需求組
rg.find();
//增加一個需求
rg.add();
//要求變更計劃
rg.plan();
}
}
執行結果:
場景類中客戶找到了需求組,向需求組提出了增加需求和需求變更的要求。
這樣存在什麼問題呢?甲方對乙方做一些要求的時候,要找到對應的組,但是不能希望甲方是非常專業的,可能提需求提到了開發組頭上,要求頁面變更提到了需求組頭上。
使用命令模式後
這是為什麼專案需要專案經理的原因之一。
甲方不管你什麼需求、美工、開發,只管把專案經理叫過去,告訴專案經理他們的要求。專案經理再根據甲方的要求向各個組下達命令。對應的,就可以引入命令模式。
來看看具體的程式碼:
- 抽象命令類:
public abstract class Command {
//把三個組都定義好,子類可以直接使用
protected RequirementGroup rg = new RequirementGroup(); //需求組
protected PageGroup pg = new PageGroup(); //美工組
protected CodeGroup cg = new CodeGroup(); //程式碼組
//只有一個方法,你要我做什麼事情
public abstract void execute();
}
- 增加需求的命令:
public class AddRequirementCommand extends Command{
@Override
public void execute() {
//找到需求組
super.rg.find();
//增加一份需求
super.rg.add();
//給出計劃
super.rg.plan();
}
}
- 刪除頁面的命令:
public class DeletePageCommand extends Command{
@Override
public void execute() {
//找到頁面組
super.pg.find();
//刪除一個頁面
super.rg.delete();
//給出計劃
super.rg.plan();
}
}
- 負責人Invoker:負責人只要接到客戶的命令,就立刻執行。
public class Invoker {
// 什麼命令
private Command command;
// 客戶發出命令
public void setCommand(Command command) {
this.command = command;
}
// 執行客戶的命令
public void action() {
this.command.execute();
}
}
- 場景類:模擬客戶增加需求的命令。
public class Client {
public static void main(String[] args) {
// 定義呼叫者
Invoker jingli = new Invoker();
// 客戶要求增加一項需求
System.out.println("------------客戶要求增加一項需求---------------");
// 客戶下命令
Command command = new AddRequirementCommand();
// 呼叫者接收到命令
jingli.setCommand(command);
// 呼叫者執行命令
jingli.action();
}
}
場景類就簡單了很多,如果客戶提出其它要求,如刪除頁面,只需要:
// 客戶下命令
//Command command = new AddRequirementCommand();
Command command = new DeletePageCommand();
同樣比較簡單。
這樣一來,客戶提要求的時候,不需要知道具體誰去完成這個要求,只需求把這個要求提給專案經理即可。
高內聚的要求就被滿足了。
命令模式優缺點
優點
● 類間解耦
呼叫者角色與接收者角色之間沒有任何依賴關係,呼叫者實現功能時只需呼叫Command 抽象類的execute方法就可以,不需要了解到底是哪個接收者執行。
● 可擴充套件性 Command的子類可以非常容易地擴充套件,而呼叫者Invoker和高層次的模組Client不產生嚴 重的程式碼耦合。
缺點
使用命令模式可能會導致某些系統有過多的具體命令類。因為針對每一個對請求接收者的調 用操作都需要設計一個具體命令類,因此在某些系統中可能需要提供大量的具體命令類,這 將影響命令模式的使用。
命令模式使用場景
在以下情況下可以考慮使用命令模式:
- 系統需要將請求呼叫者和請求接收者解耦,使得呼叫者和接收者不直接互動。請求呼叫者 無須知道接收者的存在,也無須知道接收者是誰,接收者也無須關心何時被呼叫。
- 系統需要在不同的時間指定請求、將請求排隊和執行請求。一個命令物件和請求的初始調 用者可以有不同的生命期,換言之,最初的請求發出者可能已經不在了,而命令物件本身仍 然是活動的,可以通過該命令物件去呼叫請求接收者,而無須關心請求呼叫者的存在性,可 以通過請求日誌檔案等機制來具體實現。
- 系統需要支援命令的撤銷(Undo)操作和恢復(Redo)操作。
參考:
【1】:《Java設計模式之禪》
【2】:《design-pattern-java》
【3】:《研磨設計模式》