命令模式-將請求封裝成對象


公號:碼農充電站pro
主頁:https://codeshellme.github.io

本篇來介紹命令模式Command Design Pattern),它將“請求”封裝成對象,從而將“請求”的創建者與“請求”的執行者解耦。

1,一次購物流程

相信大家都在網上買過東西,我們以淘寶為例來介紹命令模式。

我們假設這樣一個簡單的場景:

  • 淘寶網有很多商店,商店售賣各種各樣的商品,顧客購買商品需要先在淘寶下訂單
  • 一位顧客想在淘寶購買一部華為手機,他下了一個訂單:“一部華為手機”。
  • 淘寶網將該訂單發送到華為商店。
  • 華為商店將華為手機寄給了顧客。

在這個過程中,淘寶網並不關心每家商店的具體情況,它只知道每家商店都能完成它所派發的訂單。

那么,我們怎樣為這個場景建模呢?

2,模擬購物流程

從上面購物流程中,我們能看出來,連接顧客淘寶網商店的中間橋梁是訂單

  • 顧客在淘寶網生成一個訂單。
  • 淘寶網將訂單發給具體商店。
  • 商店將商品寄給顧客以完成訂單。

首先,我們需要定義一個 Order 接口:

interface Order {
    void execute();
}

Order 接口中只有一個 execute 方法,需要派生類來實現。

然后定義一個華為商店:

abstract class Shops {
    protected String shopName; 
    protected abstract String sell();
}

class HuaWeiShop extends Shops {

    public HuaWeiShop() {
        this.shopName = "HUAWEI";
    }

    public String sell() {
        return "HuaWei Phone";
    }
}

上面代碼中,Shops 是一個抽象類,表示商店,商店可以銷售商品。HuaWeiShop 類繼承了 Shops 接口,實現了 sell 方法。

然后定義一個 GoodsOrder 類,它繼承了 Order 接口,並實現了 execute 方法:

class GoodsOrder implements Order {
    private Shops shop;

    public GoodsOrder(Shops shop) {
        this.shop = shop;
    }

    public void execute() {
        String goods = shop.sell();
        System.out.println(goods);
    }
}

然后定義 Client 類,用於生成訂單:

class Client {
    public Order createOrder() {
        Shops phone = new HuaWeiShop();
        Order phoneOrder = new GoodsOrder(phone);
        return phoneOrder;
    }
}

下面定義淘寶網,它可以接收訂單和處理訂單:

class Taobao {
    private Order order;

    public void receiveOrder(Order order) {
        this.order = order;
    }

    // 處理訂單
    public void handleOrder() {
        order.execute();
    }
}

最后來測試代碼:

Client c = new Client();
Order order = c.createOrder(); // 顧客生成訂單

Taobao t = new Taobao();       
t.receiveOrder(order);         // 淘寶接收訂單
t.handleOrder();               // 淘寶處理訂單

輸出如下:

HuaWei Phone

輸出表示顧客成功買到了手機。

我們畫出上面代碼的類圖,如下:

在這里插入圖片描述

我將完整的命令模式代碼放在了這里,供大家參考。

3,命令模式

實際上,上面代碼的實現方式就是命令模式

命令模式將請求(命令)封裝為一個對象,這樣可以將不同請求注入到其他對象,並且能夠支持請求(命令)的排隊執行、記錄日志、撤銷等功能

命令模式中包含以下幾個組件(並把組件類比到上面的購物場景中):

  • Invoker:請求的調用者,相當於 Taobao
  • Command:一個抽象接口,定義了所有具體請求必須實現的方法,相當於 Order
  • ConcreteCommand:一個具體的請求,相當於 GoodsOrder
  • Receiver:請求的接收者,用於真正的執行請求,相當於 HuaWeiShop
  • Client:請求的創建者。

命令模式的類圖如下(與上面購物代碼的類圖一致):

在這里插入圖片描述

上圖的 Command 接口中有一個 undo 方法,它是 execute 方法的反操作,用於實現撤銷功能

命令模式通過將請求封裝成對象,將請求的創建者,請求的調用者和請求的執行者,這三者之間徹底解耦:

  • Client 只負責請求的創建,而不關心請求何時何地被真正的執行。
  • Invoker 只負責調用請求,而不關心請求是誰創建的,也不關心請求的真正執行者是誰。
    • Invoker 也可以將一個請求拋棄掉,不去調用它 。
  • Receiver 只負責執行請求,而不關心請求是誰創建的,也不關心請求是被誰調用的。

4,請求服務

請求服務是一種由客戶端發出請求,然后由服務端去處理的一種程序架構,不同的客戶端之間互不干擾。

我們上面模擬的購物程序可以說使用的就是這種架構,如下:

在這里插入圖片描述

比如 Redis Server 處理 Client 命令的方式使用的就是這種架構。

5,請求隊列

請求被封裝成對象后,可將其放在請求隊列中,然后由工作線程將其取出,再執行。

這種架構也相當於一個生產者-消費者架構。

在這里插入圖片描述

6,請求日志

請求被封裝成對象后,也可以將其記錄在日志中。如果服務意外崩潰,服務重啟后就可以使用請求日志,將服務恢復到崩潰之前的狀態。

在這里插入圖片描述

比如 RedisAOF 持久化使用的就是這種方式。

7,總結

命令模式將請求封裝成對象,有兩個優點:

  • 使得請求的創建者與請求的調用/執行者解耦。
  • 使得請求可以輕松的被傳遞和存儲

命令模式的這些優點,使得我們可以實現請求的排隊執行、記錄日志等功能。

(本節完。)


推薦閱讀:

單例模式-讓一個類只有一個對象

工廠模式-將對象的創建封裝起來

策略模式-定義一個算法族

觀察者模式-將消息通知給觀察者

裝飾者模式-動態的包裝原有對象的行為


歡迎關注作者公眾號,獲取更多技術干貨。

碼農充電站pro


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM