---
title: 'Command 命令模式'
disqus: kyleAlien
---
Command 命令模式
===
## Overview of Content
Command 命令模式是一種**行為模式**,它將一系列的方法封裝在一個類,也就是將**請求封裝成一個物件**
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)
:::
[TOC]
## Command 模式 - 概述
Command 命令模式是一種 **++行為模式設計++**
可以用於封裝一系列的操作,而使用者只須要執行一個方法,而不必知道細節
:::warning
* **這有點類似於 Factory 設計** ?
1. `Factory` 設計 也會封裝細節不讓使用者知道,但 `Command` 設計 的封裝自由度更高,會把行為拆分,並自由讓使用者調整順序
2. 兩者個目的不同,`Command 模式` 目標是修改行為,`Factory 設計` 則是創建一個對象
:::
:::warning
* **這有點類似於 `Template` 設計** ?
`Template 設計` 會按照一個模板,來操作一系列 **固定** 的行為,自由度較低,`Command 設計` 則較為自由,可隨意調整順序
:::
### Command 使用場景
* Command 命令模式 特性是高內聚,減少使用者使用類的成本
* 對請求排入佇列 (Queue),需要儲存命令的 Log 日誌
* 透過 `呼叫者 Invoker` 與 `執行者 RealControl` 進行解偶,對每一個方法封裝給呼叫者使用,使用者不會直接控制到執行者
### Command 定義 & UML
* Command 模式定義
將 **請求裝成一個 ++對象++**,讓用戶使用不同的請求,把客戶端參數化;既然參數化,那就可以把請求進行調整,像是排序、撤銷
* Command UML 核心重點類
| 類 | 關係 | 其他 |
| -------- | -------- | - |
| Invoker | 給使用者使用,需要 **注入包裝後的方法**,用於包裝命令 | 可記錄方法調用 |
| ICommand(抽象方法) | 抽象執行方法 | **參數化 `RealControl` 的行為** |
| ConcreateCommand | 實做命令(並不是實做,只是命令的實做),會對應 1~ 多個 `RealControl` 類 | 可做加強,紀錄時間等等 |
| RealControl | 邏輯的底層程式 | 也稱為接收者 (Receiver) |
:::info
* 在標準的 `Command` 模式下 User 會有兩種行為
1. 手動創建實際命令 `RealControl 類` 的實體(`instance`)
2. 再將命令注入到 `Invoker 類`
:::
> ![](https://hackmd.io/_uploads/ryk2wbBSh.png)
### Command 優缺點
* **Command 設計的優點**:
* 對於方法的調用屬於弱耦合、擴充性、靈活性更強,可以依據需求隨意調整調動
* **Command 設計的缺點**:
* 很明顯的類的膨脹(基本上是所有設計模式的通病)
* 它會加重程式的複雜度,需要思考一下業務需求是否有需要這樣設計,避免設計過度的問題
## Command 實現
### Command 標準
1. **`ICommand` 接口**:將**執行動作抽象化**,最終每個行為會成為一個參數,並讓 Invoker 聚合、操作這些行為 (參數)
> **命令參數化的基礎上再將其抽象化**!
```java=
// ICommand.java
public interface ICommand {
void execute();
}
```
2. **`ConcreteCommand` 類**:**^1.^ 實作執行這個行為 Function,^2.^ 內部存有實際邏輯的對象`RealControl`,但並 ==不是實際邏輯==**
> 這個類代表了 **命令參數化** 的實做!
:::success
* 這一層把真正的命令包裝起來,可以控制命令真正執行的時間、執行方式、運行環境... 等等!
:::
```java=
public class ConcreteCommand implements ICommand {
private final RealControl realControl;
public ConcreteCommand(RealControl realControl) {
this.realControl = realControl;
}
@Override
public void execute() {
realControl.action();
}
}
```
3. **`RealControl` 類**:實際邏輯 (具體操作行為)
```java=
public class RealControl {
public void action() {
System.out.println("Real Controller");
}
}
```
4. **`Invoker` 類**:聚合 1 ~ 多個 `ICommand` 對象,控制多個行為 (操作順序)
```java=
public class Invoker {
private final List<ICommand> commandList = new ArrayList<>();
public void addCommand(ICommand iCommand) {
commandList.add(iCommand);
}
public void start() {
for(ICommand cmd : commandList) {
cmd.execute();
}
}
}
```
### Command 優化 - 迪米特、依賴倒置
* **Command 設計模式優化的目標有如下**
1. **迪米特**:減少使用者對於 `RealControl` 類的認知
2. **依賴倒置**:`AbstactCommand` 也就是抽象類,讓其依賴於抽象 `RealControl` 而不是實體 `ConcreteRealControl` 類
> ![](https://hackmd.io/_uploads/r10pw-SSh.png)
:::info
這裡依照最基本的 Command 設計實作一個 RPG Game 的上、下、左、右操作
:::
1. **`RealControl` 抽象**:符合依賴倒置原則,抽象化實做指令(抽象化執行者)
```java=
// RealControl.java
public abstract class GameControl {
public abstract void action();
}
```
2. **`ConcreteRealControl` 類**:實際指令 **實作細節**
```java=
public class TurnRight extends GameControl {
@Override
public void action() {
System.out.println("Right");
}
}
// -------------------------------------------------------
public class TurnLeft extends GameControl {
@Override
public void action() {
System.out.println("Lift");
}
}
// -------------------------------------------------------
public class TurnUp extends GameControl {
@Override
public void action() {
System.out.println("Up");
}
}
// -------------------------------------------------------
public class TurnDown extends GameControl {
@Override
public void action() {
System.out.println("Down");
}
}
```
3. **`AbstractCommand` 抽象類**:簡單的抽象化一個行為(抽象化調用者),準備 **參數化 `GameControl` 的行為**(也就是 `RealControl` 角色)
```java=
// ICommand.java
public abstract class AbstractCommand {
protected GameControl gameControl;
public AbstractCommand(GameControl gameControl) {
this.gameControl = gameControl;
}
// 走的步數
public void execute(long step) {
System.out.println("----------- Start command");
while (step-- > 0) { // 閉區間,0 ~ (step -1)
exec(gameControl);
}
}
// 抽象化調用者
abstract void exec(GameControl control);
}
```
4. **`ConcreteCommand` 類**:針對每個指令 (上、下、左、右) 實作,**每個 ++指令的參數化++**,並直接寫入實做類
```java=
// ConcreteCommand.java
// 左轉
public class LeftCommand extends AbstractCommand {
public LeftCommand() {
super(new TurnLeft());
}
@Override
public void exec(GameControl control) {
control.turnLeft();
}
}
// ------------------------------------------------------------
// 右轉
public class RightCommand extends AbstractCommand {
public RightCommand() {
super(new TurnRight());
}
@Override
public void exec(GameControl control) {
control.turnRight();
}
}
// ------------------------------------------------------------
// 向上
public class UpCommand extends AbstractCommand {
public UpCommand() {
super(new TurnUp());
}
@Override
public void exec(GameControl control) {
control.turnUp();
}
}
// ------------------------------------------------------------
// 向下
public class DownCommand extends AbstractCommand {
public DownCommand() {
super(new TurnDown());
}
@Override
public void exec(GameControl control) {
control.turnDown();
}
}
```
4. **`Invoker` 類**:**聚合** 所有要操作的指令,透過 Invoker 操作實際命令
:::info
* **符合迪米特原則**(最少知識)
不用讓使用者知道真正個實做類,只須知道某個指令
* 這裡使用外部 DI 指令,當然 **我們也可以把指令直接創建在 `Invoker` 之內**
:::
```java=
public class Invoker {
private final Map<String, AbstractCommand> map = new HashMap<>();
public Invoker(AbstractCommand right, AbstractCommand left, AbstractCommand up, AbstractCommand down) {
map.put("Right", right);
map.put("Left", left);
map.put("Up", up);
map.put("Down", down);
}
// 指令化
public void turnRight(long step) {
AbstractCommand right = map.get("Right");
if(right == null) {
return;
}
right.execute(step);
}
// 指令化
public void turnLeft(long step) {
AbstractCommand left = map.get("Left");
if(left == null) {
return;
}
left.execute(step);
}
// 指令化
public void turnUp(long step) {
AbstractCommand up = map.get("Up");
if(up == null) {
return;
}
up.execute(step);
}
// 指令化
public void turnDown(long step) {
AbstractCommand down = map.get("Down");
if(down == null) {
return;
}
down.execute(step);
}
}
```
* **作為 User 使用 `Command` 設計模式**:創建實作類,再創建 Invoker,將實作類注入 Invoker
```java=
public class MainCommand {
public static void main(String[] args) {
// 將實作類注入 Invoker
Invoker invoker = new Invoker(
new RightCommand(),
new LeftCommand(),
new UpCommand(),
new DownCommand());
invoker.turnDown(3);
invoker.turnUp(1);
invoker.turnLeft(2);
invoker.turnRight(5);
}
}
```
> ![](https://i.imgur.com/9FMX3qZ.png)
### Command 拓展 - 添加紀錄
* 若要紀錄 `Command` 也十分簡單,由於我們參數化具體命令,所以只需要在 User 操做的 `Invoker` 類添加 `LinkedList` 做紀錄即可
1. 首先先修改 `ICommand`(`AbstractCommand`)抽象類,多出一個共用 Function,用來記錄上次移動的 Step
```java=
// AbstractCommand.java
public abstract class AbstractCommand {
private long step;
protected GameControl gameControl;
public AbstractCommand(GameControl gameControl) {
this.gameControl = gameControl;
}
public void execute(long step) {
// 紀錄最後一次移動的步數
this.step = step;
System.out.println("----------- Start command");
while (step-- > 0) {
exec(gameControl);
}
}
abstract void exec(GameControl control);
// 新增 Function
public void doLastAgain() {
execute(step);
}
}
```
2. 在 `Invoker` 內添加 `LinkedList` 做紀錄(也可以用來排隊、儲存)
:::info
使用 `LinkedList` 可以按照順序做 Step 移動紀錄(這個數據結構對於動態操作元素的行為較為友善,效率較高),也可以作為 Log 紀錄
:::
```java=
// Invoker.java
public class Invoker {
private final LinkedList<AbstractCommand> record = new LinkedList<>();
private final Map<String, AbstractCommand> map = new HashMap<>();
public Invoker(AbstractCommand right, AbstractCommand left, AbstractCommand up, AbstractCommand down) {
map.put("Right", right);
map.put("Left", left);
map.put("Up", up);
map.put("Down", down);
}
... 省略上、下、左、右操作
public void showRecordStep() {
AbstractCommand tmp = record.getFirst();
while (tmp != null) {
tmp.doLastAgain();
tmp = record.poll();
}
}
}
```
> ![](https://i.imgur.com/90NhL5R.png)
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)
* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)
* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::
### 創建模式 - Creation Patterns
* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)
* **創建模式 - `Creation Patterns`**:
創建模式用於「**物件的創建**」,它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節,並提供創建對象的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
:::success
* [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)
* [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)
* [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)
* [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
* [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)
* [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)
* [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
:::
### 行為模式 - Behavioral Patterns
* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)
* **行為模式 - `Behavioral Patterns`**:
行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
:::warning
* [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)
* [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)
* [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)
* [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)
* [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)
* [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)
* [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)
* [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)
* [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)
* [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)
* [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
:::
### 結構模式 - Structural Patterns
* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)
* **結構模式 - `Structural Patterns`**:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
:::danger
* [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)
* [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)
* [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)
* [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)
* [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)
* [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
:::
## Appendix & FAQ
:::info
:::
###### tags: `Java 設計模式` `基礎進階`