命令模式

目錄

1 命令模式

命令模式將”請求”封裝成對象(指命令對象,能把方法調用封裝起來),以便使用不同的請求、隊列或者日誌來參數化其他對象(指調用者對象)。命令模式也支持可撤銷的操作。

在這裏插入圖片描述

  • 一個命令對象Command需要綁定一個命令接收者Receiver,將接收者接收者的具體動作封裝進來,只暴露出一個execute()方法,當此方法被調用時,就會執行接收者的動作。這樣,當調用者對象調用命令對象的execute()方法時,不需要知道是哪個接收者接收到了請求命令也不需要知道接收者執行了哪些動作
  • 通過一個抽象基類的命令接口類,可以擴展出許多具體的命令類,這樣就只需要使用接口類對象來參數化調用者對象即可。調用者對象不需要知道具體的命令,只知道他是一個命令即可
  • 可以通過命令模式來實現“隊列、日誌和支持撤銷操作”
    在安裝嚮導、訂單系統、遙控控制系統、日程安排、工作隊列等諸多現實生活場景中有應用。

2 命令模式的UML類圖

在這裏插入圖片描述

  • Client: 客戶端類,創建一個具體的命令對象ConcreteCommand和接收者對象Receiver,並將接收者對象綁定到具體的命令對象中。
  • Receiver: 接收者類,接收者具有執行請求命令的具體方法action()
  • Command: 命令對象的抽象基類,所有的具體命令對象都需要實現此接口,擁有一個execute()方法,調用此方法就可以讓接收者進行相關的動作,調用undo()方法可以讓接收者進行撤銷動作。
  • ConcreteCommand: 具體的命令對象類,實現了Command接口。與接收者綁定起來,調用者調用命令對象的execute()方法就能發出請求命令,然後由命令接收者Receiver接收命令執行相關的動作action()
  • Invoker: 調用者類,傳入一個具體的命令對象參數到方法setCommand()中,並在某個時間點調用命令對象的execute()方法

使用步驟:

  • 客戶端實例化一個接收者對象receiver一個具體的命令對象command將receiver綁定到command中
  • 實例化一個調用者invoker,並在某個時間點使用一個命令對象傳入到setCommand(command)方法中,然後調用命令對象command的execute()方法發出請求命令
  • 在命令對象的execute()方法中,調用與其綁定的接收者的action()方法,執行命令

優勢

  • 命令模式將發出請求的調用者對象執行請求的接收者對象解耦`。
  • 解耦的兩個對象通過命令對象溝通命令對象封裝了接收者的一個或多個動作
  • 調用者調用命令對象的execute()方法發出請求,這使得接收者的動作被調用。
  • 命令對象可以支持撤銷,做法是實現一個 undo()方法來回到execute()被執行前的狀態
  • 宏命令使用一系列命令對象初始化,然後調用多個命令

3 觀察者的一個例子:遙控家電

遙控器有7個插槽,每個插槽對應兩個按鈕,每個按鈕對應到一個命令,這樣遙控器就充當了調用者的身份。當按下按鈕時,相應命令對象的execute()方法被調用,與其綁定的接收者(例如“電燈”、“天花板電扇”、“音響”)的動作就會被調用。還有一個撤銷按鈕,當按下時調用命令對象的undo()方法,接收者執行相關的動作。
在這裏插入圖片描述

3.1 基本功能實現在這裏插入圖片描述

3.2 撤銷按鈕功能實現

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

3.3 宏命令功能實現

創建一個命令對象,當調用此命令對象的execute()方法後,會執行一系列命令。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

4 隊列請求和日誌請求

4.1 隊列請求

如果有一個工作隊列:在一端添加命令,在另一端則是線程。線程進行下面的動作:從工作隊列中選擇一個命令,然後調用execute()方法,等待調用完成後,將此命令對其,再取出下一個命令。兩個相鄰的命令之間無需有任何關係。前一個命令可以是讀取網絡數據,下一個命令可以是處理財務運算。

4.2 日誌請求

某些應用需要將所有動作記錄在日誌中。當系統死機時,重新調用這些動作恢復到之前的狀態。只需要在命令中新增兩個方法store()和load()即可完成。

5 命令模式的一個Python實現例子

5.1 例子解釋

假如你是證券交易所的客戶,會創建買入股票和賣出股票的訂單(即Command)。通常情況下,你通過代理或經紀人(即Invoker)向證券交易所(即Receiver)溝通,而不是直接到證券交易所執行交易。代理負責將你的請求提交給證券交易所,完成股票買入或賣出請求。

5.2 UML類圖

在這裏插入圖片描述

5.3 代碼實現

  • 接收者類StockTrade
    • 定義了具體的動作buy()和sell(),由具體命名對象的execute()調用此方法,然後執行具體動作
class StockTrade:
    '''
    Receiver接受者,是一個知道如何做必要的工作的類
    '''
    def buy(self):
        print("You will buy stocks.")
        
    def sell(self):
        print("You will sell stocks.")
  • 命令接口類Order
    • 抽象基類,具有一個execute()方法

from abc import ABCMeta, abstractmethod
class Order(metaclass=ABCMeta):
    '''
    命令Command的抽象基類,即爲具體的命令類提供了一個接口
    '''
    @abstractmethod
    def execute(self):
        '''通過調用命令對象的execute()方法,可以讓接收者進行相應的工作'''
        pass```
  • 具體的命令對象類BuyStockOrder、SellStockOrder
    • 首先構造函數__init__(stock)使用一個接收者對象stock初始化屬性stock
    • 實現的execute()方法調用stock的buy()或sell()方法
    
class BuyStockOrder(Order):
    '''具體命令對象類'''
    def __init__(self, stock):
        '''通過此方法將接收者綁定'''
        self.stock = stock
        
    def execute(self):
        '''命令執行方法,通過調用此方法`發出命令執行請求`,然後`調用接收者的一個或多個動作`,由`接收者執行請求`'''
        self.stock.buy()
    
    
class SellStockOrder(Order):
    '''具體命令對象類'''
    def __init__(self, stock):
        '''通過此方法將接收者綁定'''
        self.stock = stock
        
    def execute(self):
        '''命令執行方法,通過調用此方法`發出命令執行請求`,然後`調用接收者的一個或多個動作`,由`接收者執行請求`'''
        self.stock.sell()
    
    
  • 調用者類Agent
    • 首先構造函數__init__()初始化__orderQueue屬性爲一個空列表
    • 傳入一個具體命令對象給placeOrder(order)方法,並在某一時刻調用此方法發出請求然後執行命令對象的execute()方法在execute()方法中,執行接收者的動作完成請求
class Agent:
    '''
    Invoker調用者
    '''
    def __init__(self):
        self.__orderQueue = []
        
    
    def placeOrder(self, order):
        '''調用者擁有一個命令對象列表,在某個時刻調通過此方法調用命令對象的execute()方法,將請求付諸實行'''
        self.__orderQueue.append(order)
        order.execute()
        
  • 客戶端
    • 客戶端實例化一個接收者對象stock具體的命令對象buyStock、sellStock將stock綁定到具體命令中
    • 實例化一個調用者agent,並在某個時間點將一個命令對象傳入到placeOrder(order)方法中,然後調用命令對象的execute()方法發出請求命令
    • 在命令對象的execute()方法中,調用與其綁定的接收者的buy()方法或sell()方法,執行命令
if __name__ == '__main__':
    # Client
    stock = StockTrade() # 客戶先實例化一個接收者對象
    buyStock = BuyStockOrder(stock) # 然後將接收者對象與具體的命令對象綁定起來
    sellStock = SellStockOrder(stock) # 將接收者對象與具體的命令對象綁定起來
    
    # Invoker
    agent = Agent() # 實例化一個調用者對象
    agent.placeOrder(buyStock) # 將命令對象與調用者綁定起來,調用者`發出命令執行請求`
    agent.placeOrder(sellStock) # 將命令對象與調用者綁定起來,`發出命令執行請求`

5.4 輸出結果

在這裏插入圖片描述
github地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章