軟件系統(tǒng)在接受到指令后,通常需要執(zhí)行各種各樣的操作。例如文本處理軟件的用戶通過用戶界面發(fā)出各種指令,他們想打開一個(gè)文檔,保存一個(gè)文檔,打印一個(gè)文檔,復(fù)制一段文本,粘貼一段復(fù)制的文本等。這種通用的模式在其他的領(lǐng)域也存在,例如 ,在金融領(lǐng)域中,客戶可以向證券交易商發(fā)出購買股票、出售股票等請求。在制造業(yè)這樣的技術(shù)領(lǐng)域,命令被用來控制工業(yè)設(shè)備和機(jī)器。
在實(shí)現(xiàn)由命令控制的軟件系統(tǒng)時(shí),重要的是保證操作的請求者與實(shí)際執(zhí)行操作的對象分離。這背后的指導(dǎo)原則是松耦合原則和關(guān)注點(diǎn)分離的原則。
餐館就是一個(gè)很好的類比。在餐館中,服務(wù)員接受顧客點(diǎn)的菜,但服務(wù)員不負(fù)責(zé)做飯,做飯是廚房的事情。事實(shí)上,對于顧客來說,食物的制作過程是透明的,也許是餐廳準(zhǔn)備食物,也許是食物從其他地方運(yùn)送過來。
在面向?qū)ο蟮能浖_發(fā)中,由一種名為Command(Action)的行為模式可以促進(jìn)這種分離。其任務(wù)說明如下:
將請求封裝為對象,從而允許你使用不同的請求、隊(duì)列或日志的請求參數(shù)化客戶端,或支持可撤銷操作。
命令模式的一個(gè)很好的例子是Clinet/Server架構(gòu)體系,其中Client(即所謂的調(diào)用者)發(fā)送命令給Server,Server(即所謂的接收者或被調(diào)用者)接受并執(zhí)行命令。
讓我們從一個(gè)抽象的Command類開始,它是一個(gè)簡單的小接口:
#pragma once
#include
//一個(gè)抽象的Command類,它是一個(gè)簡單的小接口
class Command
{
public:
virtual ~Command() = default;
virtual void execute() = 0;
};
//為指向命令的智能指針引入了一個(gè)類型別名(CommandPtr)
using CommandPtr = std::shared_ptr;
// 這個(gè)抽象的Command接口可以由各種具體的命令實(shí)現(xiàn),
#pragma once
#include"Command.h"
#include
// 一個(gè)非常簡單的具體的命令的實(shí)現(xiàn)
// 抽象的Command接口可以由各種具體的命令實(shí)現(xiàn),先看一個(gè)簡單的命令——輸出字符串“Hello World!”
class HelloWorldOutputCommand :public Command
{
virtual void execute() override
{
std::cout<< “Hello World\n”<< std::endl;
}
};
#pragma once
#include"Command.h"
//命令接收者
// 需要接受并執(zhí)行命令的元素,在這個(gè)設(shè)計(jì)模式中,這個(gè)元素被稱為Receiver,在
// 我們的例子中,扮演這個(gè)角色的是一個(gè)名為Server的類。
// 目前,該類只包含一個(gè)可以接受和執(zhí)行命令的簡單公共成員函數(shù)。
class Server
{
public:
void acceptCommand(const CommandPtr& command)
{
command->execute();
}
};
#pragma once
// 最后,我們需要所謂的Invoker, 即在Client/Server架構(gòu)中的Client類;
// 給Server 發(fā)送命令的Client類
#include"Server.h"
#include"HelloWorldOutputCommand.h"
class Client
{
public:
void run()
{
Server theServer{};
CommandPtr helloWorldOutputCommand = std::make_shared();
theServer.acceptCommand(helloWorldOutputCommand);
}
};
#include"Client.h"
// main() 函數(shù)
int main()
{
Client client{};
client.run();
return 0;
}
編譯執(zhí)行這個(gè)程序,在標(biāo)準(zhǔn)輸出控制臺就會輸出“Hello World!”字符串。通過Command模式實(shí)現(xiàn)的是,命令的初始化和發(fā)送與命令的執(zhí)行是分離的。
由于這種設(shè)計(jì)模式支持開放—封閉(OCP)原則,添加新的命令非常容易,只需對現(xiàn)有代碼進(jìn)行微小的修改即可實(shí)。例如,如果想強(qiáng)制服務(wù)器等待一段時(shí)間,可以添加以下代碼:
#pragma once
#include"Command.h"
#include
#include
class WaitCommand :public Command
{
public:
explicit WaitCommand(const unsigned int durationInMilliseconds) noexcept:
durationInMillseconds{ durationInMilliseconds }{};
virtual void execute() override
{
std::chrono::milliseconds dur{ durationInMillseconds };
std::this_thread::sleep_for(dur);
}
private:
unsigned int durationInMillseconds{ 1000 };
};
現(xiàn)在,我們可以像下面這樣使用這個(gè)新的WaitCommand類:
#pragma once
// 最后,我們需要所謂的Invoker, 即在Client/Server架構(gòu)中的Client類;
// 給Server 發(fā)送命令的Client類
#include"Server.h"
#include"HelloWorldOutputCommand.h"
#include"WaitCommand.h"
class Client
{
public:
/void run()
{
Server theServer{};
CommandPtr helloWorldOutputCommand = std::make_shared();
theServer.acceptCommand(helloWorldOutputCommand);
}/
void run()
{
Server theServer{};
const unsigned int SERVER_DELAY_TIMESPAN{ 3000 };
CommandPtr waitCommand = std::make_shared(SERVER_DELAY_TIMESPAN);
theServer.acceptCommand(waitCommand);
CommandPtr helloWorldOutputCommand = std::make_shared();
theServer.acceptCommand(helloWorldOutputCommand);
}
};
為了對上述討論的類結(jié)構(gòu)有一個(gè)大致的了解,下圖描述了對應(yīng)的UML類圖。
正如在這個(gè)示例中看到的,我們可以使用值參數(shù)化命令,由于純虛execute()成員函數(shù)的簽名是由Command接口指定為無參的,因此參數(shù)化是在初始化構(gòu)造函數(shù)的幫助下完成的。此外,我們不需要Server類,因?yàn)樗梢粤⒓刺幚砗蛨?zhí)行新擴(kuò)展的命令。
Command 模式提供了應(yīng)用程序的多種可能性。例如,可以排隊(duì),也支持命令的異步執(zhí)行:Invoker發(fā)送命令然后立即執(zhí)行其他的操作,發(fā)送的命令稍后由Receiver執(zhí)行。
然而,缺少了一些東西!在上面引用的Command模式的任務(wù)聲明中,可以讀到一些關(guān)于“……支持可撤銷操作”的內(nèi)容。
在上一節(jié)的 Client/Server 體系結(jié)構(gòu)的示例中,實(shí)際上,服務(wù)器不會像上面演示的那樣執(zhí)行命令,到達(dá)服務(wù)器的命令對象將被分布到負(fù)責(zé)執(zhí)行命令的服務(wù)器的內(nèi)部。例如,可以在另一種稱為 職責(zé)鏈的設(shè)計(jì)模式的幫助下完成。
考慮一個(gè)稍微復(fù)雜一點(diǎn)的例子,假設(shè)我們有一個(gè)繪圖程序,用戶可以用該程序繪制許多不同的形狀,例如,圓形和矩形。為此,可以調(diào)用用戶界面相應(yīng)的菜單進(jìn)行操作。即:熟悉的軟件開發(fā)人員通過Command設(shè)計(jì)模式執(zhí)行這些繪圖操作。然而,利益相關(guān)者指出用戶也可以撤銷繪圖操作。
為了滿足這個(gè)需求,首先我們需要有可撤銷的命令。
//UndoableCommand接口通過組合Command和Revertable實(shí)現(xiàn)
class Command
{
public:
virtual ~Command() = default;
virtual void execute() = 0;
};
class Revertable
{
public:
virtual ~Revertable() = default;
virtual void undo() = 0;
};
class UndoCommand :public Command, public Revertable{};
using CommandPtr = std::shared_ptr;
根據(jù)接口隔離原則,我們添加了另一個(gè)支持撤銷功能的Revertable接口。UndoableCommand類同時(shí)繼承現(xiàn)有的Command接口和新增的Revertable接口。
有許多不同的撤銷繪圖的命令,將畫圓作為具體的例子:
一個(gè)可以撤銷畫圓的命令
#include"Command.h"
#include"DrawingProcessor.h"
#include"Point.h"
// 一個(gè)可以撤銷畫圓的命令
class DrawingCircleCommand :public UndoablesCommand
{
public:
DrawingCircleCommand(DrawingProcessor& receiver,const Point& centerPoint ,const double radius)noexcept:
receiver{ receiver }, centerPoint{ centerPoint }, radius{ radius }{}
virtual void execute() override {
receiver.drawCircle(centerPoint, radius);
}
virtual void undo() override {
receiver.eraseCircle(centerPoint, radius);
}
private:
DrawingProcessor& receiver;
const Point centerPoint;
const double radius;
};
很容易想象得出來,繪制矩形和其他形狀得命令和繪制圓形得命令看起來非常相似。命令得執(zhí)行者是一個(gè)名為DrawingProcessor的類,這指執(zhí)行繪圖操作的元素,在構(gòu)造命令對象時(shí),會將該對象的引用與其他參數(shù)一起傳遞給構(gòu)造函數(shù)。
DrawingProcessor類是處理繪圖操作的元素
#pragma once
#include"Point.h"
// DrawomgProcessor類是處理繪圖操作的元素
class DrawingProcessor
{
public:
void drawCircle(const Point& centerPoint, const double radius)
{
// instructions to draw a circle on the screen…
}
void eraseCircle(const Point& centerPoint, const double radius)
{
// Instructions to erase a circle from the screen…
}
};
現(xiàn)在來看這個(gè)模式的核心部分CommandProcessor:
#pragma once
#include
#include"Command.h"
//CommandProcessor管理可撤銷命令對象的一個(gè)堆棧
class CommandProcessor
{
public:
void execute(const CommandPtr& command)
{
command->execute();
commandHistory.push(command);
}
void undoLastCommand()
{
if (commandHistory.empty())
{
return;
}
commandHistory.top()->undo();
commandHistory.pop();
}
private:
//std::stack
std::stack< std::shared_ptr>commandHistory;
};
CommandProcessor類(順便說一下,上面的類不是線程安全的)包含了std::stack(定義在頭文件中),它是一種支持LIFO(后進(jìn)先出)的抽象的數(shù)據(jù)類型。執(zhí)行了CommandProcessor::execute()成員函數(shù)后,相應(yīng)的命令會被存儲到commandHistory堆棧中,當(dāng)調(diào)用CommandProcessor::undoLastCommand()成員函數(shù)時(shí),存在堆棧上的最后一個(gè)命令就會被撤銷,然后從堆棧頂部刪除。
同樣,現(xiàn)在可以將撤銷操作建模為命令對象,在這種情況下,命令接收者就是CommandProcessor本身:
UndoCommand 類為CommandProcessor提供撤銷操作
#pragma once
// UndoCommand 類為CommandProcessor提供撤銷操作
#include"Command.h"
#include"CommandProcessor.h"
class UndoCommand :public UndoableCommand
{
public:
explicit UndoCommand(CommandProcessor& receiver) noexcept :
receiver(receiver) {}
virtual void execute() override
{
receiver.undoLastCommand();
}
virtual void undo() override
{
// intentionally left blank, because an undo should not be undone.
}
private:
CommandProcessor& receiver;
};
在實(shí)際使用Command模式時(shí),常常需要能夠從幾個(gè)簡單的命令組合成一個(gè)更復(fù)雜的命令,或者記錄和回放命令(腳本)。為了能夠方便地實(shí)現(xiàn)這些需求,下面的Composite模式比較合適。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
名稱欄目:【設(shè)計(jì)模式——Command模式】-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://chinadenli.net/article20/deccjo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、網(wǎng)頁設(shè)計(jì)公司、域名注冊、微信小程序、外貿(mào)建站、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容