設計模式精講 Day 14:命令模式(Command Pattern)

【設計模式精講 Day 14】命令模式(Command Pattern)


文章內容

在“設計模式精講”系列的第14天,我們來學習命令模式(Command Pattern)。命令模式是一種行為型設計模式,它將請求封裝為對象,從而使你可以用不同的請求、隊列或日志來參數化客戶端。這種模式的核心思想是通過解耦請求的發起者和執行者,提升系統的靈活性與可擴展性。

命令模式在實際開發中廣泛應用,例如在GUI事件處理、任務調度、事務管理等領域。它能夠幫助開發者構建更清晰、更易于維護的系統架構。本篇文章將從理論到實踐,深入講解命令模式的定義、結構、實現方式以及實際應用案例,并結合Java代碼展示其具體使用方式。


模式定義

命令模式(Command Pattern)是一種行為型設計模式,它將一個請求封裝成一個對象,從而使你可以在不同的請求之間進行參數化傳遞,或者將請求排隊、記錄日志、撤銷操作等。

核心思想
將請求的發送者與接收者解耦,使得請求可以被參數化、記錄、撤銷或重放。


模式結構

命令模式的UML類圖包含以下幾個關鍵角色:

  • Command(命令接口):聲明執行操作的抽象方法。
  • ConcreteCommand(具體命令):實現Command接口,通常會持有接收者(Receiver)的引用,并調用其業務方法。
  • Receiver(接收者):知道如何執行與請求相關的操作。
  • Invoker(調用者):向客戶端提供調用命令的方法,通常不直接與接收者交互。
  • Client(客戶端):創建具體的命令對象,并設置其接收者。
類圖文字描述
+---------------------+
|     Command         |
+---------------------+
| + execute()         |
+---------------------+^|
+---------------------+
| ConcreteCommand     |
+---------------------+
| - receiver: Receiver|
| + execute()         |
+---------------------+^|
+---------------------+
|     Receiver        |
+---------------------+
| + action()          |
+---------------------+^|
+---------------------+
|     Invoker         |
+---------------------+
| + setCommand()      |
| + executeCommand()  |
+---------------------+

適用場景

命令模式適用于以下幾種典型場景:

場景描述
請求的參數化需要將請求作為參數傳遞給其他對象
請求的隊列將多個請求放入隊列中異步執行
日志記錄記錄所有操作以便后續恢復或調試
撤銷/重做功能通過保存命令對象實現操作的回滾
事件驅動系統在GUI或消息隊列中處理用戶輸入或消息

實現方式

下面是一個完整的Java代碼示例,演示了命令模式的基本實現。

1. 定義命令接口
/*** 命令接口:定義執行操作的抽象方法*/
public interface Command {void execute();
}
2. 定義接收者類
/*** 接收者:執行具體業務邏輯的對象*/
public class Receiver {public void action() {System.out.println("Receiver is performing the action.");}
}
3. 定義具體命令類
/*** 具體命令類:實現Command接口,持有接收者的引用*/
public class ConcreteCommand implements Command {private Receiver receiver;public ConcreteCommand(Receiver receiver) {this.receiver = receiver;}@Overridepublic void execute() {// 調用接收者的業務方法receiver.action();}
}
4. 定義調用者類
/*** 調用者:負責調用命令對象的execute方法*/
public class Invoker {private Command command;public void setCommand(Command command) {this.command = command;}public void executeCommand() {if (command != null) {command.execute();} else {System.out.println("No command to execute.");}}
}
5. 客戶端代碼
/*** 客戶端:創建命令對象并設置接收者*/
public class Client {public static void main(String[] args) {Receiver receiver = new Receiver();Command command = new ConcreteCommand(receiver);Invoker invoker = new Invoker();invoker.setCommand(command);invoker.executeCommand(); // 輸出:Receiver is performing the action.}
}

工作原理

命令模式通過將請求封裝為對象,實現了請求的解耦。調用者(Invoker)不再直接與接收者(Receiver)交互,而是通過命令對象間接調用。這使得系統更加靈活,可以輕松地添加新的命令類型而無需修改現有代碼。

此外,由于命令對象是獨立的,它們可以被存儲、傳遞、重復使用甚至撤銷。例如,在實現撤銷功能時,只需要保存命令對象的歷史記錄即可。


優缺點分析

優點缺點
解耦請求的發送者與接收者增加了系統的復雜度
支持請求的排隊、日志和撤銷可能導致過多的命令類
易于擴展新的命令類型增加了內存消耗(每個命令都是一個對象)

案例分析:任務調度系統

假設我們正在開發一個任務調度系統,需要支持異步執行任務、記錄日志和撤銷操作。使用命令模式可以很好地解決這些問題。

問題描述:
  • 用戶提交任務后,需要異步執行
  • 需要記錄每條任務的執行日志
  • 需要支持撤銷最近一次操作
解決方案:

使用命令模式封裝每個任務,由調度器統一管理。每次執行任務時,同時記錄日志;撤銷操作時,只需調用對應的命令對象的undo()方法。

示例代碼:
// 擴展命令接口,增加 undo 方法
public interface Command {void execute();void undo();
}// 修改接收者
public class TaskReceiver {public void executeTask() {System.out.println("Executing task...");}public void undoTask() {System.out.println("Undoing task...");}
}// 修改具體命令類
public class TaskCommand implements Command {private TaskReceiver receiver;private boolean executed = false;public TaskCommand(TaskReceiver receiver) {this.receiver = receiver;}@Overridepublic void execute() {if (!executed) {receiver.executeTask();executed = true;}}@Overridepublic void undo() {if (executed) {receiver.undoTask();executed = false;}}
}// 修改調用者
public class TaskScheduler {private Command currentCommand;public void setCommand(Command command) {this.currentCommand = command;}public void executeCommand() {if (currentCommand != null) {currentCommand.execute();}}public void undoCommand() {if (currentCommand != null) {currentCommand.undo();}}
}// 客戶端測試
public class TaskClient {public static void main(String[] args) {TaskReceiver receiver = new TaskReceiver();Command command = new TaskCommand(receiver);TaskScheduler scheduler = new TaskScheduler();scheduler.setCommand(command);scheduler.executeCommand(); // 執行任務scheduler.undoCommand();    // 撤銷任務}
}

在這個案例中,命令模式不僅提升了系統的可擴展性,還使任務的執行、記錄和撤銷變得非常簡單。


與其他模式的關系

命令模式常與其他設計模式結合使用,以增強系統功能:

模式說明
責任鏈模式命令模式可以作為責任鏈中的一個節點,實現請求的鏈式處理
備忘錄模式命令模式可以與備忘錄模式配合,用于實現撤銷功能
迭代器模式可以將命令對象封裝為迭代器,實現對命令集合的遍歷
組合模式命令模式可以嵌套使用,形成復雜的操作序列

總結

今天我們詳細學習了命令模式(Command Pattern)。通過將請求封裝為對象,命令模式實現了請求的解耦,提高了系統的靈活性和可維護性。我們在Java中通過完整的代碼示例展示了該模式的實現方式,并結合任務調度系統的實際案例進行了分析。

命令模式體現了面向對象設計中的單一職責原則(SRP)和開閉原則(OCP),因為每個命令類只關注自己的業務邏輯,且可以通過擴展命令類來滿足新需求。

在接下來的Day 15中,我們將進入**解釋器模式(Interpreter Pattern)**的學習,探索如何為語言或表達式定義文法并解析其含義。敬請期待!


文章標簽

design-patterns, java, command-pattern, software-design, object-oriented-programming


文章簡述

本文深入講解了“設計模式精講”系列的第14天——命令模式(Command Pattern)。文章從理論出發,介紹了命令模式的核心思想、結構組成和適用場景,并通過完整的Java代碼示例展示了其基本實現方式。我們還通過一個任務調度系統的實際案例,說明了命令模式在真實項目中的應用價值。此外,文章還對比了命令模式與其他相關設計模式的關系,并分析了其優缺點。通過對命令模式的全面講解,讀者可以更好地理解如何在實際開發中利用這一模式提高系統的靈活性和可維護性。


進一步學習資料

  1. Design Patterns: Elements of Reusable Object-Oriented Software
  2. Refactoring Guru - Command Pattern
  3. Java Design Patterns - Command Pattern
  4. Martin Fowler’s Patterns of Enterprise Application Architecture - Command
  5. Java Design Patterns Book - Command Pattern Example

核心設計思想總結

命令模式的核心在于將請求封裝為對象,從而實現請求的參數化、記錄、撤銷等功能。在實際項目中,我們可以使用命令模式來構建任務調度系統、GUI事件處理、事務管理等模塊,顯著提升系統的可擴展性和可維護性。通過將請求的發起者與執行者解耦,命令模式讓系統更加靈活,也更容易應對未來的變化。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87097.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87097.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87097.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

手機射頻功放測試學習(二)——手機線性功放的靜態電流和小信號(S-Parameter)測試

目錄 一、概要 二、LPA的電流測試 1、LPA的泄漏電流測試 手動測試步驟如下: 自動化測試: 2、LPA的靜態電流測試 手動測試步驟如下: 自動化測試: 三、LPA的S-Parameter測試 1、矢量網絡分析儀校準 2、LPA的S參數手動測試步驟: 3、LPA的S參數自動測試步驟: 四…

基礎算法合集-圖論

本文將介紹數據結構圖論部分中常見的算法 單源最短路徑問題(用來計算一個點到其他所有頂點的最短路徑) Dijkstra(n*n) 1. 初始化: 先找出從源點V0到各終點Vk的直達路徑(V0,Vk), 即通過一條弧到達的路徑 2. 選擇: 從這些路徑中找出一條長度最短的路徑(V0,u) 3. 更新: 然后對其余…

vue-i18n 插件打包解析失效問題記錄

vue-i18n 插件打包解析失效問題記錄 開發環境中沒有問題的,但打包發布之后就不行了,顯示的就是模板字符串 // An highlighted block const messages {en: {step: {stepDesc1: Scan,stepDesc2: Analyze,stepDesc3: Result}},zh: {step: {stepDesc1: 掃描…

數據可視化 - 單子圖

一、認識單子圖 import matplotlib.pyplot as plt import numpy as np import pandas as pdplt.figure(num單子圖, figsize(12, 8), facecolorw) # 中文字體 plt.rcParams[font.sans-serif] KaiTi # 負號顯示 plt.rcParams[axes.unicode_minus] False# 2行,1列&a…

服務器上設置了代理之后,服務器可以訪問外網,但是不能訪問服務器本地。如何解決

你在服務器上設置了代理后,發現: 可以訪問外網不能訪問服務器本地地址(如 localhost、127.0.0.1、內網IP) 這是代理設置中常見的問題,尤其是當你設置了全局 HTTP/HTTPS 代理時。本地訪問也會被強制走代理&#xff0c…

mysql啟動報錯:Can‘t connect to local MySQL server through socket

文章目錄 一、報錯內容二、解決方法 一、報錯內容 在linux上啟動mysql時報錯 [rootlocalhost bin]# ./mysql -u root -p Enter password: ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock (2)執行以上命令后報錯,并且也…

C# Avalonia 綁定模式 Mode 的區別,它們的應用場景

C# Avalonia 綁定模式 Mode 的區別,它們的應用場景 文章目錄 1. **Default(默認模式)**2. **OneTime(一次性綁定)**3. **OneWay(單向綁定)**4. **TwoWay(雙向綁定)**5. *…

【OpenGL學習】(七)紋理單元

【OpenGL學習】(七)紋理單元 OpenGL的紋理單元(Texture Unit)是GPU中用于管理和組織紋理資源的邏輯單元,它允許開發者在渲染過程中同時使用多個紋理,并通過采樣器(Sampler)在著色器…

Ubuntu 下降 Linux Kernel 的版本備忘

此處以 ubuntu 22.04 為示例系統,來降低其 Linux kernel 的版本。 1. 降低 Linux kernel 版本 在 Ubuntu 22.04 上降低 Linux 內核版本的步驟如下所示。 步驟 1:檢查當前內核版本 uname -r 確認當前運行的內核版本。 步驟 2:查看已安裝的…

Python 數據分析與機器學習入門 (八):用 Scikit-Learn 跑通第一個機器學習模型

引言:初識 Scikit-Learn Scikit-learn 是 Python 機器學習領域的黃金標準庫。它構建在 NumPy, SciPy 和 Matplotlib 之上,提供了大量用于分類、回歸、聚類和降維等任務的算法。Scikit-learn 廣受歡迎的原因在于其三大核心優勢: 一致的 API 設…

FPGA芯片的配置方法

FPGA芯片的配置方法 文章目錄 FPGA芯片的配置方法1. FPGA配置概述2. 主動配置模式3. 被動配置模式4. JTAG配置模式5. 總結 1. FPGA配置概述 當我們在PC機上的FPGA軟件集成開發環境中完成我們的設計后,必須通過某種形式將其映射到FPGA芯片硬件中,這樣FPG…

通過python+openCV實現對圖片中箭頭方向的判斷

在項目中遇到一個需求,需要對圖片中的箭頭方向進行判斷,本來是使用YOLOv8算法來實現的,但是發現YOLO的效果對箭頭的識別效果很差,不管是分類算法還是檢測算法,效果都不理想,因此試一試通過openCV對箭頭方向進行判斷,發現效果還可以。 下面附上完整的代碼和原理。 文章目…

React 第六十六節Router中 StaticRouter使用詳解及注意事項

前言 StaticRouter 是 React Router 為服務器端渲染(SSR)提供的專用路由組件。它允許在服務器環境中處理路由邏輯,確保服務器和客戶端渲染結果一致。下面我將詳細解釋其用途、原理并提供完整的代碼示例。 一、StaticRouter 的核心用途 服務…

嵌入模型與大語言模型的區別:從結構到應用的深度解析

嵌入模型與大語言模型的區別:從結構到應用的深度解析 在當今自然語言處理(NLP)技術蓬勃發展的背景下,嵌入模型(Embedding Model) 和 大語言模型(Large Language Model, LLM) 成為了…

el-date-picker賦值不成功

vue使用element 的時間組件el-date-picker賦值不成功,點擊后才回顯數據 解決: 組件未渲染完成之前賦值了,在onMounted函數內賦值,或者在確保組件已經渲染后賦值

深入淺出JavaScript中的私有變量與特權方法

深入淺出JavaScript中的私有變量與特權方法:封裝的藝術 在JavaScript的開發實踐中,私有變量和特權方法是實現數據封裝和代碼安全性的核心工具。它們不僅幫助我們隱藏敏感數據,還能通過閉包和作用域機制構建更健壯的代碼結構。本文將從基礎概…

ReactNative【實戰系列教程】我的小紅書 2 -- 快捷登錄、手機號密碼登錄

最終效果 技術要點 用戶協議 – 打開本地瀏覽器 點擊后,直接打開本地瀏覽器瀏覽網頁 // 最終需修改為 《用戶協議》 的網址Linking.openURL("https://www.baidu.com");手機號輸入框的 344 展示 onChangeText{(text: string) > {setPhone(formatPhone(…

【趙渝強老師】OceanBase數據庫從零開始:Oracle模式

這里我們來介紹一下新上線的課程《OceanBase數據庫從零開始:Oracle模式》,本門課程共11章。 視頻講解如下 【趙渝強老師】OceanBase從零開始(Oracle模式) 下面詳細介紹一下每一章的主要內容: 第01章-OceanBase的體系…

Flink核心功能與運行流程詳解

目錄 一、背景 二、圖構建 三、任務執行流程(yarn per-job模式) 3.1 Flink組件 3.2 執行流程 四、分布式調度 4.1 TM的slot 4.2 TM的slot的CPU與內存 4.3 節點的部署 4.4 節點的狀態 4.5 節點部署流程 五、數據傳輸 5.1 內存分配 5.2 傳輸…

linux 操作docker的基本命令docker倉庫

基本操作命令 docker run --nametest-host -itd centos7.6 /bin/bash 通過鏡像創建容器 登錄容器 [rootdocker101 ~]# docker exec -it test-host /bin/bash (exec是執行,i是交互式。t叫tty) 或者container id [rootdocker101 ~]# doc…