遊戲程式設計模式學習:第一章命令模式

Ryaneo 發表於 2020-11-22

命令模式:將一個請求封裝成一個物件,從而允許你使用不同的請求,佇列或者日誌將客戶端引數化,同
時支援請求操作的撤銷與恢復;

主要目的

該設計模式主要是為了隔離請求和實際執行者之間接觸,實現兩者的解耦。所有的請求統一有一個類負責
該類負責管理這些請求。這樣命令物件和接收者之間的耦合度就會降低。命令物件不用直接施加命令到接收者上。
而會通過施加命令到命令介面,通過命令介面例項化出來一個相應的命令,從而施加到接收者上。

具體例子:

1.我們去餐廳吃飯,我們是通過服務員來點菜,具體是誰來做這些菜和他們什麼時候完成的這些菜,其實我們都不知道。抽象之,我們是“選單請求者”,廚師是“選單實現者”,2者之間是鬆耦合的,我們對這些菜的其他一些請求比如“撤銷,重做”等,我們也不知道是誰在做。其實這就是本文要說的Command模式。將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤消的操作。[GOF 《設計模式》]

#include <iostream>  
#include <string>
#include <vector>
using namespace std;
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }//安全刪除

//燒烤師傅類,最後的執行者
class Barbecuer
{
public:
	void BakeMutton()
	{
		cout << "Bake Mutton" << endl;
	}
	void BakeChickenWing()
	{
		cout << "Bake ChickenWing" << endl;
	}
};

//抽象命令類:執行具體操作的介面
class Command
{
public:
	Command() {};
	Command(Barbecuer* receiver):p_receiver(receiver) {}
	virtual void ExecuteCommand() = 0;
protected:
	Barbecuer* p_receiver;
};


//具體命令,烤羊肉串命令
class BakeMuttonCommand:public Command
{
public :
	BakeMuttonCommand(Barbecuer* receiver)
	{
		p_receiver = receiver;
	}
	void ExecuteCommand()
	{
		p_receiver->BakeMutton();
	}
};


//具體命令,烤雞翅命令
class BakeChickenWingCommand :public Command
{
public:
	BakeChickenWingCommand(Barbecuer* receiver)
	{
		p_receiver=receiver;
	}
	void ExecuteCommand()
	{
		p_receiver->BakeChickenWing();
	}
};


//服務員類,相當於命令者
class Waiter
{
public:
	void SetOrder(Command* command);//相當於一個命令佇列
	void Notify();
private:
	vector<Command*>p_Commandlist;
};


//按照順序給佇列內新增相應的命令
void Waiter::SetOrder(Command* command)
{
	p_Commandlist.push_back(command);

	cout << "增加烤肉命令" << endl;
}


//按順序執行所有命令
void Waiter::Notify()
{
	vector<Command*>::iterator i;
	for (i = p_Commandlist.begin(); i < p_Commandlist.end(); ++i)
	{
		(*i)->ExecuteCommand();
	}

}

int main(int argc, char* argv[])
{

	//生成訂單,燒烤師傅,服務員
	Barbecuer* p_cook = new Barbecuer();
	Command* p_mutton = new BakeMuttonCommand(p_cook);
	Command* p_chicken = new BakeChickenWingCommand(p_cook);
	Waiter* p_waiter = new Waiter();

	//將頂點新增到佇列中
	p_waiter->SetOrder(p_mutton);
	p_waiter->SetOrder(p_chicken);


	//服務員通知訂單開始執行
	p_waiter->Notify();
	SAFE_DELETE(p_cook);
	SAFE_DELETE(p_mutton);
	SAFE_DELETE(p_chicken);
	SAFE_DELETE(p_waiter);

	return 0;
}

2.單人回合制的遊戲中,我們想讓玩家能夠撤銷一些行動一邊能夠更多專注與策略而不是猜測。我們可以將所有的動作封裝起來。
虛擬碼如下:


class Command
{
public:
  virtual ~Command() {}
  virtual void execute() = 0;
  virtual void undo() = 0;
};

class MoveUnitCommand : public Command
{
public:
  MoveUnitCommand(Unit* unit, int x, int y)
  : unit_(unit),
    xBefore_(0),
    yBefore_(0),
    x_(x),
    y_(y)
  {}

  virtual void execute()
  {
    // 儲存移動之前的位置
    // 這樣之後可以復原。

    xBefore_ = unit_->x();
    yBefore_ = unit_->y();

    unit_->moveTo(x_, y_);
  }

  virtual void undo()
  {
    unit_->moveTo(xBefore_, yBefore_);
  }

private:
  Unit* unit_;
  int xBefore_, yBefore_;
  int x_, y_;
};

參考資料:《遊戲程式設計模式》,《設計模式》