C++設計模式之命令模式(command)(行爲型)

一 定義

命令模式(Command):將一個請求封裝爲一個對象,從而使你可用不同的請求對客戶進行參數化,對請求進行排隊或記錄請求日誌,以及支持可撤銷的操作。

    其本質是對命令的抽象和封裝,把通常的命令發起——>接收到命令並執行過程,通過增加一箇中間角色變爲命令發起——>中介負責先接收命令再轉發——>接收到命令再執行過程,簡而言之,就是命令發起者可以將很多命令放進中間角色, 它並不知道功能是如何實現的,只負責轉發給真正的命令執行者。

二 ULM圖

 

角色說明:

抽象命令類(Command): 聲明執行操作的接口。調用接收者相應的操作,以實現執行的方法Execute。
具體命令類(ConcreteCommand): 創建一個具體命令對象並設定它的接收者。通常會持有接收者,並調用接收者的功能來完成命令要執行的操作。 
調用者(Invoker): 要求該命令執行這個請求。通常會持有命令對象,可以持有很多的命令對象。
接收者(Receiver): 知道如何實施與執行一個請求相關的操作。任何類都可能作爲一個接收者,只要它能夠實現命令要求實現的相應功能。 
客戶類(Client): 創建具體的命令對象,並且設置命令對象的接收者。真正使用命令的客戶端是從Invoker來觸發執行。

Command模式優點:

1) 降低系統的耦合度:Command模式將調用操作的對象與知道如何實現該操作的對象解耦。

2) Command是頭等的對象。它們可像其他的對象一樣被操縱和擴展。

3) 組合命令:你可將多個命令裝配成一個組合命令,即可以比較容易地設計一個命令隊列和宏命令。一般說來,組合命令是Composite模式的一個實例。

4) 增加新的Command很容易,因爲這無需改變已有的類。

5)可以方便地實現對請求的Undo和Redo。

命令模式的缺點:

使用命令模式可能會導致某些系統有過多的具體命令類。因爲針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。

實例1:

燒烤店有烤羊肉串和烤雞翅。對於就餐的客戶來說,只需要告訴服務員需要點什麼,而不關心點的餐怎麼做。

 

 

代碼實現:

#include <iostream>
#include <string>
#include <list>

//Receiver:此處爲烤羊肉串者
class Barbecuer
{
public:
	void BakeMutton()
	{
		std::cout << "烤羊肉串嘍!" << std::endl;
	}
	void BakeChickenWing()
	{
		std::cout << "烤雞翅嘍!" << std::endl;
	}
};

//Command類,抽象命令
class Command
{
protected:
	Barbecuer* receiver;
public:
	Command(Barbecuer* receiver)
	{
		this->receiver = receiver;
	}
	virtual void ExcuteCommand() = 0;
};

//ConcreteCommand類,具體命令
class BakeMuttonCommand :public Command
{
public:
	BakeMuttonCommand(Barbecuer* receiver) :Command(receiver) {}
	void ExcuteCommand()
	{
		receiver->BakeMutton();
	}
};

//ConcreteCommand類,具體命令
class BakeChickenWingCommand :public Command
{
public:
	BakeChickenWingCommand(Barbecuer* receiver) :Command(receiver) {}
	void ExcuteCommand()
	{
		receiver->BakeChickenWing();
	}
};

//Invoker:此處爲Waiter服務員
class Waiter
{
private:
	std::list<Command*>* orders;
public:
	Waiter()
	{
		orders = new std::list<Command*>;
	}
	~Waiter()
	{
		delete orders;
	}

	//設置訂單
	void SetOrder(Command* command)
	{
		//判斷命令的類型並分別做不同的處理
		if (typeid(*command) == typeid(BakeChickenWingCommand))
		{
			std::cout << "日誌:服務員:雞翅沒有了,請點別的燒烤!" << std::endl;
		}
		else if (typeid(*command) == typeid(BakeMuttonCommand))
		{
			orders->push_back(command);
			std::cout << "日誌:增加訂單:命令模式.烤羊肉串  " << std::endl;
		}
		else
		{
			std::cout << "日誌:暫時沒有該服務!" << std::endl;
		}
	}

	//通知全部執行
	void Notify()
	{
		std::list<Command*>::iterator it;
		for (it = orders->begin(); it != orders->end(); it++)
		{
			(*it)->ExcuteCommand();
		}
	}

	//取消訂單,這裏僅僅是爲了演示,真實需要根據訂單號來取消
	void CancelOrder(Command* command)
	{
		
		if (typeid(*command) == typeid(BakeChickenWingCommand))
		{
			orders->remove_if([](const auto& item) { return typeid(*item) == typeid(BakeChickenWingCommand); });
			std::cout << "取消訂單:烤雞翅" << std::endl;
		}
		else if (typeid(*command) == typeid(BakeMuttonCommand))
		{
			orders->remove_if([](const auto& item) { return typeid(*item) == typeid(BakeChickenWingCommand); });
			std::cout << "取消訂單:烤羊肉串" << std::endl;
		}
	}
};

int main()
{
	//開店前的準備
	Barbecuer* boy = new Barbecuer();

	Command* bakeMuttonCommand = new BakeMuttonCommand(boy);
	Command* bakeChickenWingCommand = new BakeChickenWingCommand(boy);

	Waiter* girl = new Waiter();

	//開門營業,顧客點菜
	girl->SetOrder(bakeMuttonCommand);
	girl->SetOrder(bakeChickenWingCommand);
	girl->CancelOrder(bakeChickenWingCommand);

	//點菜完畢,通知廚房
	girl->Notify();

	delete boy, bakeMuttonCommand, bakeMuttonCommand, bakeChickenWingCommand;
	
	return 0;
}

運行結果:

 

實例2:

電視機遙控器 :

電視機是請求的接收者, 遙控器是請求的發送者, 遙控器上有一些按鈕,不同的按鈕對應電視機的不同操作。抽象命令角色由一個命令接口來扮演, 有三個具體的命令類實現了抽象命令接口,這三個具體命令類分別代表三種操作:打開電視機、關閉電視機和切換頻道。

 顯然,電視機遙控器就是一個典型的命令模式應用實例。

ULM圖:

 代碼實現:

#include <iostream>
#include <memory>

//Receiver: Television
class Television 
{
public:
	void open() 
	{
		std::cout << "打開電視機!" << std::endl;
	}

	void close() 
	{
		std::cout << "關閉電視機!" << std::endl;
	}

	void changeChannel() 
	{
		std::cout << "切換電視頻道!" << std::endl;
	}
};

//AbstartCommand
class AbstractCommand 
{
public:
	virtual void execute() = 0;
};

//Concrete commmand: TVOpenCommand
class TVOpenCommand : public AbstractCommand 
{
public:
	TVOpenCommand(std::shared_ptr<Television> tv)
	{
		this->tv = tv;
	}

	void execute() 
	{
		tv->open();
	}

private:
	std::shared_ptr<Television> tv;
};

//Concrete command: TVCloseCommmand
class TVCloseCommand : public AbstractCommand 
{
public:
	TVCloseCommand(std::shared_ptr<Television> tv) 
	{
		this->tv = tv;
	}

	void execute() 
	{
		tv->close();
	}

private:
	std::shared_ptr<Television> tv;
};

//Concrete command: TVChangeCommmand
class TVChangeCommand : public AbstractCommand 
{
public:
	TVChangeCommand(std::shared_ptr<Television> tv) 
	{
		this->tv = tv;
	}

	void execute() 
	{
		tv->changeChannel();
	}

private:
	std::shared_ptr<Television> tv;
};

//Invoker: Controller
class Controller 
{
public:
	Controller(std::shared_ptr<AbstractCommand> openCommand,
		std::shared_ptr<AbstractCommand> closeCommand,
		std::shared_ptr<AbstractCommand> changeCommand) {
		this->openCommand = openCommand;
		this->closeCommand = closeCommand;
		this->changeCommand = changeCommand;
	}

	void open()
	{
		openCommand->execute();
	}

	void change() 
	{
		changeCommand->execute();
	}

	void close() 
	{
		closeCommand->execute();
	}
private:
	std::shared_ptr<AbstractCommand> openCommand;
	std::shared_ptr<AbstractCommand> closeCommand;
	std::shared_ptr<AbstractCommand> changeCommand;
};

//客戶端測試
int main(void) {
	// 接收者電視機 
	std::shared_ptr<Television> tv = std::make_shared<Television>();

	// 命令 
	std::shared_ptr<AbstractCommand> openCommand = std::make_shared<TVOpenCommand>(tv);
	std::shared_ptr<AbstractCommand> closeCommand = std::make_shared<TVCloseCommand>(tv);
	std::shared_ptr<AbstractCommand> changeCommand = std::make_shared<TVChangeCommand>(tv);

	// 調用者
	std::shared_ptr<Controller> controll = std::make_shared<Controller>(openCommand, closeCommand, changeCommand);

	// 測試 
	controll->open();
	controll->change();
	controll->close();

	return 0;
}

 運行結果:

 

 

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