1. 程式人生 > >C++設計模式之命令模式

C++設計模式之命令模式

概述:

        我們去餐廳吃飯,我們是通過服務員來點菜,具體是誰來做這些菜和他們什麼時候完成的這些菜,其實我們都不知道。抽象之,“選單請求者”我們和“選單實現者”廚師,2者之間是鬆耦合的,我們對這些菜的其他一些請求比如“撤銷,重做”等,我們也不知道是誰在做。其實這就是本文要說的Command模式。

        將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤消的操作。[GOF 《設計模式》]

類圖與例項:


角色用途:

客戶(Client)角色:建立了一個具體命令(ConcreteCommand)物件並確定其接收者。

命令(Command)角色:聲明瞭一個給所有具體命令類的抽象介面。這是一個抽象角色。

具體命令(ConcreteCommand)角色:定義一個接受者和行為之間的弱耦合;實現Execute()方法,負責呼叫接收考的相應操作。Execute()方法通常叫做執行方法。

請求者(Invoker)角色:負責呼叫命令物件執行請求,相關的方法叫做行動方法。

接收者(Receiver)角色:負責具體實施和執行一個請求。任何一個類都可以成為接收者,實施和執行請求的方法叫做行動方法。

例項:

  1. #include <iostream>
  2. #include <vector>
  3. using
    namespace std;  
  4. // 烤肉師傅
  5. class RoastCook  
  6. {  
  7. public:  
  8.     void MakeMutton() { cout << "烤羊肉" << endl; }  
  9.     void MakeChickenWing() { cout << "烤雞翅膀" << endl; }  
  10. };  
  11. // 抽象命令類
  12. class Command  
  13. {  
  14. public:  
  15.     Command(RoastCook* temp) { receiver = temp; }  
  16.     virtualvoid ExecuteCmd() = 0;  
  17. protected:  
  18.     RoastCook* receiver;  
  19. };  
  20. // 烤羊肉命令
  21. class MakeMuttonCmd : public Command  
  22. {  
  23. public:  
  24.     MakeMuttonCmd(RoastCook* temp) : Command(temp) {}  
  25.     virtualvoid ExecuteCmd() { receiver->MakeMutton(); }  
  26. };  
  27. // 烤雞翅膀命令
  28. class MakeChickenWingCmd : public Command  
  29. {  
  30. public:  
  31.     MakeChickenWingCmd(RoastCook* temp) : Command(temp) {}  
  32.     virtualvoid ExecuteCmd() { receiver->MakeChickenWing(); }  
  33. };  
  34. // 服務員類
  35. class Waiter  
  36. {  
  37. public:  
  38.     void SetCmd(Command* temp);  
  39.     // 通知執行
  40.     void Notify();  
  41. protected:  
  42.     vector<Command*> m_commandList;  
  43. };  
  44. void Waiter::SetCmd(Command* temp)  
  45. {  
  46.     m_commandList.push_back(temp);  
  47.     cout << "增加訂單" << endl;  
  48. }  
  49. void Waiter::Notify()  
  50. {  
  51.     vector<Command*>::iterator it;  
  52.     for (it=m_commandList.begin(); it!=m_commandList.end(); ++it)  
  53.     {  
  54.         (*it)->ExecuteCmd();  
  55.     }  
  56. }  
  57. int main()  
  58. {  
  59.     // 店裡新增烤肉師傅、選單、服務員等顧客
  60.     RoastCook* cook = new RoastCook();  
  61.     Command* cmd1 = new MakeMuttonCmd(cook);  
  62.     Command* cmd2 = new MakeChickenWingCmd(cook);  
  63.     Waiter* girl = new Waiter();  
  64.     // 點菜
  65.     girl->SetCmd(cmd1);  
  66.     girl->SetCmd(cmd2);  
  67.     // 服務員通知
  68.     girl->Notify();  
  69.     return 0;  
  70. }  

效果與實現要點:

1Command模式的根本目的在於將行為請求者行為實現者解耦,在面嚮物件語言中,常見的實現手段是將行為抽象為物件

2.實現Command介面的具體命令物件ConcreteCommand有時候根據需要可能會儲存一些額外的狀態資訊。

3.通過使用Compmosite模式,可以將多個命令封裝為一個複合命令”MacroCommand

4Command模式與C#中的Delegate有些類似。但兩者定義行為介面的規範有所區別:Command以面向物件中的介面-實現來定義行為介面規範,更嚴格,更符合抽象原則;Delegate以函式簽名來定義行為介面規範,更靈活,但抽象能力比較弱。

5.使用命令模式會導致某些系統有過多的具體命令類。某些系統可能需要幾十個,幾百個甚至幾千個具體命令類,這會使命令模式在這樣的系統裡變得不實際。

適用性:

在下面的情況下應當考慮使用命令模式:

1.使用命令模式作為"CallBack"在面向物件系統中的替代。"CallBack"講的便是先將一個函式登記上,然後在以後呼叫此函式。

2.需要在不同的時間指定請求、將請求排隊。一個命令物件和原先的請求發出者可以有不同的生命期。換言之,原先的請求發出者可能已經不在了,而命令物件本身仍然是活動的。這時命令的接收者可以是在本地,也可以在網路的另外一個地址。命令物件可以在串形化之後傳送到另外一臺機器上去。

3.系統需要支援命令的撤消(undo)。命令物件可以把狀態儲存起來,等到客戶端需要撤銷命令所產生的效果時,可以呼叫undo()方法,把命令所產生的效果撤銷掉。命令物件還可以提供redo()方法,以供客戶端在需要時,再重新實施命令效果。

4.如果一個系統要將系統中所有的資料更新到日誌裡,以便在系統崩潰時,可以根據日誌裡讀回所有的資料更新命令,重新呼叫Execute()方法一條一條執行這些命令,從而恢復系統在崩潰前所做的資料更新。


========================================================

前言

又要過年了,又是一個搶票季;從大學起,到現在工作,一直都是在外地,離家千里;以前買票,曾經也去火車站通宵排隊買票;直到12306的騰空出現,在電腦前不停止的點著滑鼠刷票,那個時候12306很是脆弱,搶一張票更是難上加難;現在好了,慢慢強大的12306,買票時出現了一個排隊系統,先買票,進入12306的排隊系統;然後,系統一個一個的處理大家的請求,一旦你的購票請求進入了排隊系統,你就無法再次進行刷票了,除非你退出排隊系統;這就減少了購票者的刷票次數;減少了12306後臺伺服器的處理壓力。那麼,你有沒有想過,12306是如何將你的購票請求加入排隊系統的呢?這樣的排隊系統是如何實現的呢?而我今天總結的命令模式,將會對此進行簡單的剖析。

什麼是命令模式?

在GOF的《設計模式:可複用面向物件軟體的基礎》一書中對命令模式是這樣說的:將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。在OOP中,一切都是物件,將請求封裝成物件,符合OOP的設計思想,當將客戶的單個請求封裝成物件以後,我們就可以對這個請求儲存更多的資訊,使請求擁有更多的能力;命令模式同樣能夠把請求傳送者和接收者解耦,使得命令傳送者不用去關心請求將以何種方式被處理。

我們在12306上,單擊購票,這是一個請求,12306將這個請求封裝為一個物件,在12306還沒有上線排隊系統時,你買票是這樣的:你不停的用滑鼠點選12306網站上的購票按鈕,直到你買到了票;對於你的每一次點選,伺服器都要進行處理,做出響應,告訴你,有沒有買到票;這樣,可能就會出現很多次無效的點選,但是這些無效的點選卻增加了伺服器的負擔。增加了排隊系統以後,你的購票請求就進入了對應的購票佇列,一旦你進入了購票佇列,當你再次滑鼠單擊購票時,12306會拒絕你的購票請求,它會告訴你,你已經進入了購票佇列;處於購票佇列中的你,你可以選擇退出購票佇列去購買其它車次的車票,從而進入其它購票佇列。這樣就有效的減少了購票者傳送很多無效的購票請求。

這就好比票是共享資源,誰都想要,但是票的數量是一定的;在沒有排隊系統之前,大家的購票請求都是去競爭這個票,伺服器對於大家對於共享資源——票的競爭進行互斥,誰搶到了,票就少一張;而現在有了購票佇列以後,大家都不用去競爭了,按時間的先後順序排好隊,12306把票一張張的發給進入佇列的購票者。

UML類圖

果凍想 | 一個原創文章分享網站
Command:宣告執行操作的介面;
ConcreteCommand:將一個接收者物件綁定於一個動作,之後,呼叫接收者相應的操作,以實現Execute來完成相應的命令;
Client:建立一個具體命令物件,但是並沒有設定它的接收者;
Invoker:要求該命令執行這個請求;
Receiver:知道如何實施與執行一個請求相關的操作,任何類都可能作為一個接收者。

以上這些物件是按照下面的方式進行協作的:

  1. Client建立一個ConcreteCommand命令物件,並指定它的Receiver物件;
  2. Invoker物件儲存該ConcreteCommand物件;
  3. 該Invoker通過呼叫Command物件的Execute操作來提交一個請求。如果這個命令請求是可以撤銷的,ConcreteCommand就執行Execute操作之前儲存當前狀態以用於取消該命令請求;
  4. ConcreteCommand物件呼叫Receiver的一些操作以執行該請求。

使用場合

使用命令模式實現12306(工程下載):
CHomePage類,表示12306的官網訂票頁面;
C12306Processor類,是後臺真正處理使用者的請求的類,專門進行出票;
Command類,表示使用者的購票命令請求;
Customer類,表示購票的使用者。
由於程式碼較多,這裡只提供工程的下載。

這裡再提供命令模式的一般實現:

#include<iostream>usingnamespace std;#define SAFE_DELETE(p)if(p){delete p; p = NULL;}classReceiver{public:voidAction(){
          cout<<"Receiver->Action"<<endl;}};classCommand{public:virtualvoidExecute()=0;};classConcreteCommand:publicCommand{public:ConcreteCommand(Receiver*pReceiver): m_pReceiver(pReceiver){}voidExecute(){
          m_pReceiver->Action();}private:Receiver*m_pReceiver;};classInvoker{public:Invoker(Command*pCommand): m_pCommand(pCommand){}voidInvoke(){
          m_pCommand->Execute();}private:Command*m_pCommand;};int main(){Receiver*pReceiver =newReceiver();Command*pCommand =newConcreteCommand(pReceiver);Invoker*pInvoker =newInvoker(pCommand);
     pInvoker->Invoke();
     SAFE_DELETE(pInvoker);
     SAFE_DELETE(pCommand);
     SAFE_DELETE(pReceiver);return0;}

總結

命令模式是一個很經典的模式,我的理解也不會很到位;在我們的身邊,就存在很多的使用命令模式的例子,資料庫中的事務就是使用命令模式去實現的,在C#中的委託也是使用命令模式去實現的。我在這裡只是將我在學習過程中理解到的東西記錄了下來和大家分享。可能有的地方我的理解也存在差錯,希望大家和我分享你對命令模式的理解。

2014年1月17日 於大連,東軟。

============================================================================

Command命令模式
作用:將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。

由於“行為請求者”與“行為實現者”的緊耦合,使用命令模式,可以對請求排隊或記錄請求日誌,以及支援可撤銷的操作。

UML圖:


Command類,用來宣告執行操作的介面

ConcreteCommand,將一個接收者物件綁定於一個操作,呼叫接收者相應的操作,以實現Execute

Invoker類,要求該命令執行這個請求

Receiver類,知道如何實施與執行一個與請求相關的操作,任何類都可能作為一個接收者。

Command模式通過將請求封裝到一個物件Command中,並將請求的接收者存放到具體的ConcreteCommand類中,從而實現呼叫操作的物件和操作的具體實現者之間的解耦。

Command模式結構圖中,將請求的接收者(處理者)放到Command的具體子類ConcreteCommand中,當請求到來時(Invoker發出Invoke訊息啟用Command物件),ConcreteCommand將處理請求交給Receiver物件進行處理。

命令模式的優點:
1,它能較容易地設計一個命令佇列;
2,在需要的情況下,可以較容易地將命令記入日誌;
3,允許接收請求的一方決定是否要否決請求。
4,可以容易地實現對請求的撤銷和重做;
5,由於加進新的具體命令類不影響其他的類,因此增加新的具體命令類很容易。

命令模式把請求一個操作的物件與知道怎麼執行一個操作的物件分割開。

Command模式關鍵就是講一個請求封裝到一個類中(Command),再提供處理物件(Receiver),最後Command命令由Invoker啟用。另外,我們可以將請求接收者的處理抽象出來作為引數傳給Command物件,實際也就是回撥的機制來實現這一點。也就是講處理操作方法地址通過引數傳遞給Command物件,Command物件在適當的時候再呼叫該函式。

Command模式將呼叫操作的物件和知道如何實現該操作的物件解耦,在上面Command的結構圖中,Invoker物件根本就不知道具體的是哪個物件在處理Execute操作(當然要知道是Command類別的物件)。
在Command要增加新的處理操作物件很容易,我們可以通過建立新的繼承自Command的子類來實現這一點。
Command模式可以和Memento模式結合起來,支援取消的操作。

程式碼如下:

Command.h

複製程式碼
 1 #ifndef _COMMAND_H_
 2 #define _COMMAND_H_
 3 
 4 class Command
 5 {
 6 public:
 7     virtual ~Command();
 8     virtual void Execute()=0;
 9 protected:
10     Command();
11 private:
12 };
13 
14 class Receiver;
15 
16 class ConcreteCommand : public Command
17 {
18 public:
19     ConcreteCommand(Receiver* pReceiver);
20     ~ConcreteCommand();
21     virtual void Execute();
22 protected:
23 private:
24     Receiver* _recv;
25 };
26 
27 class Invoker
28 {
29 public:
30     Invoker(Command* pCommand);
31     ~Invoker();
32     void Invoke();
33 protected:
34 private:
35     Command* _cmd;
36 };
37 
38 class Receiver
39 {
40 public:
41     Receiver();
42     ~Receiver();
43     void Action();
44 protected:
45 private
            
           

相關推薦

C++設計模式命令模式

概述:         我們去餐廳吃飯,我們是通過服務員來點菜,具體是誰來做這些菜和他們什麼時候完成的這些菜,其實我們都不知道。抽象之,“選單請求者”我們和“選單實現者”廚師,2者之間是鬆耦合的,我們對這些菜的其他一些請求比如“撤銷,重做”等,我們也不知道是誰在做。其實

C++:設計模式命令模式(例子)

// 設計模式測試.cpp : 定義控制檯應用程式的入口點。 // 命令模式 #include "stdafx.h" #include <string> #include <iost

設計模式命令模式

能夠 ger 不同 exec cor del 需要 content ces 設計模式之命令模式 Feb 24, 2015 命令模式(Command)的定義是:用於將一個請求封裝成一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及執行可撤銷的

設計模式命令模式(Command)摘錄

single 而是 names 都得 結構 意圖 iterator nbsp 軟件 23種GOF設計模式一般分為三大類:創建型模式、結構型模式、行為模式。創建型模式抽象了實例化過程,它們幫助一個系統獨立於怎樣創建、組合和表示它的那些對象。一個類創建型模式使用繼承改變被實例

設計模式命令模式 Command

sta clas ide class open cli private 2017年 命令模式 介紹 角色 使用場景 代碼實現 public interface Command { //這個方法是一個返回結果為空的方法 //實際項目中,可

java設計模式命令模式

int aud 按鍵 設計 oid 定義 bsp class 命令 命令模式:   對命令的封裝,把發出命令的責任和執行命令的責任分割開,委派給不同的對象。 命令模式涉及到五個角色: 客戶端(CommandMain)角色:創建一個具體命令並確定接收者(觸發錄音機按

設計模式的藝術 行為型模式命令模式

前言 裝修新房子的最後幾道工序之一是安裝插座和開關,通過開關可以控制一些電器的開啟和關閉,例如電燈或者排氣扇,但是購買開關時其實並不知道它能夠控制什麼具體的電器。在軟體設計中,也存在著這樣的關係,請求傳送者和接收者物件,為了降低之間的耦合度,我們會將兩者進行解耦,會在兩者之間引入了新的命令物件,

設計模式命令模式(Command Pattern)

摘要 命令模式(Command Pattern)是一種資料驅動的設計模式,它屬於行為型模式。請求以命令的形式包裹在物件中,並傳給呼叫物件。呼叫 介紹 意圖:將一個請求封裝成一個物件,從而使您可以用不同的請求對客戶進行引數化。 主要解決:在軟體系統中,行為請求者與行為實現者通常是一種緊

Java 設計模式命令模式

本文為筆者學習《Head First設計模式》的筆記,並加入筆者自己的理解和歸納總結 命令模式將“請求”封裝成物件,以便使用不同的請求、佇列或者日誌來引數化其他物件。命令模式也支援可撤銷的操作。 結構圖 遙控器(RemoteControl)通過命令(Command)控制燈的開關

每天一個設計模式命令模式

作者按:《每天一個設計模式》旨在初步領會設計模式的精髓,目前採用javascript和python兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式 :) 原文地址是:《每天一個設計模式之命令模式》 歡迎關注個人技術部落格:godbmw.com。每週 1 篇原創

設計模式命令模式(Command Pattern)

命令模式定義 命令模式是一個高內聚的模式。Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requ

2.3 設計模式命令模式

命令模式 # 命令模式 // 命令基類 class command { public: command() {} virtual ~command() {} public: virtual void execute() = 0; virtual vo

ES6設計模式命令模式

模式的本質是將物件的行為和物件的呼叫者解耦。和策略模式不同的是將物件和物件的行為解耦。對於呼叫這來說只是執行excute方法就行了。這裡需要注意三部分,命令物件,呼叫者物件,被呼叫物件。 命令模式將"請求"封裝成物件,以便使用不同的請求,佇列,或者日誌來引數化其他物件。命令模式也支援撤銷操作。 程式碼如下

設計模式----命令模式

轉載自:https://www.cnblogs.com/konck/p/4199907.html雖然是.net 版的,不過不影響閱讀和學習。經典的命令模式包括4個角色:Command:定義命令的統一介面ConcreteCommand:Command介面的實現者,用來執行具體的

設計模式命令模式(一)

在本次學習過程中,我們把封裝帶到一個全新的境界:把方法呼叫(method invocation)封裝起來。沒錯,通過封裝方法呼叫,我們可以把運算塊包裝成形。 所以呼叫此運算的物件不需要關心事情是如何進行的,只要知道如何使用包裝成形的方法來完成它就可以。通過封裝方法呼叫,也可以做一些很聰明的事情,例如記錄日誌,

設計模式命令模式(二)

上一次留給大家去做的實踐,不知道大家執行的怎麼樣了呢。 我們通過一個簡單的練習,完成了一個控制開關。那現在,我們打算將遙控器的每個插槽,對應到一個命令這樣就要遙控器變成“呼叫者”。當按下按鈕,相應命令物件的execute()方法就會被呼叫,其結果就是,接收者(例如電燈、風扇、音響)的動作被呼叫。 實現遙控器

設計模式命令模式(三)

我回來啦!今天是週六,一看命令模式還有一個總結未完成,趕緊爬起來做做好。 就如上一篇所說的,如果擁有了一個遙控器,卻無法光憑按下一個按你,就同時能弄暗燈光、開啟音響和電視、設定到DVD,並讓熱水器開始加溫,那麼我要這個遙控器還有什麼意義呢? 使用巨集命令 根據比較高階的想法來看,就是我們需要製造一種新的命令,

14.java設計模式命令模式

#### 基本需求: * 一套智慧家電,有照明燈、風扇、冰箱、洗衣機,我們只要在手機上安裝app就可以控制對這些家電工作 * 這些智慧家電來自不同的廠家,我們不想針對每一種家電都安裝一個App分別控制,我們希望只要一個app就可以控制全部智慧家電 * 要實現一個app控制所有智慧家電的需要,則每個智慧家電廠

javascript設計模式詳解命令模式

這一 clas 例子 別了 logs 操作 book 技術 概念   每種設計模式的出現都是為了彌補語言在某方面的不足,解決特定環境下的問題。思想是相通的。只不過不同的設計語言有其特定的實現。對javascript這種動態語言來說,弱類型的特性,與生俱來的多態性,導致某些設

C++設計模式狀態模式(二)

virtual alt 虛構函數 需求 rate names clas term delete 2、智能空調的設計與實現 某軟件公司將開發一套智能空調系統: 系統檢測到溫度處於20---30度之間,則切換到常溫狀態;溫度處於30---45度,則切換到制冷狀態;