[英]How does the command pattern solve the problem of hard-wired commands/requests?
我正在閱讀 Robert Nystrom 的Game Programming Patterns並且對命令模式有疑問。
在配置輸入部分的第一個示例中,if/else 語句將游戲操作硬編碼到控制台按鈕:
void InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) jump();
else if (isPressed(BUTTON_Y)) fireGun();
else if (isPressed(BUTTON_A)) swapWeapon();
else if (isPressed(BUTTON_B)) lurchIneffectively();
}
由於鍵映射在編譯時是硬編碼的,因此用戶無法在運行時根據他們的偏好更改/配置它們。 然后引入命令模式作為該問題的解決方案:
// ***Command interface***
class Command
{
public:
virtual ~Command() {}
virtual void execute() = 0;
};
// ***Concrete commands***
class JumpCommand : public Command
{
public:
virtual void execute() { jump(); }
};
class FireCommand : public Command
{
public:
virtual void execute() { fireGun(); }
};
[...]
// ***Input handler***
class InputHandler
{
public:
void handleInput();
// Methods to bind commands...
private:
Command* buttonX_;
Command* buttonY_;
Command* buttonA_;
Command* buttonB_;
};
void InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) buttonX_->execute();
else if (isPressed(BUTTON_Y)) buttonY_->execute();
else if (isPressed(BUTTON_A)) buttonA_->execute();
else if (isPressed(BUTTON_B)) buttonB_->execute();
}
問題
我不清楚命令模式如何幫助使輸入映射運行時可配置。 GUI 中必須有一些表允許用戶為每個操作類型指定一個鍵:<commandName, key>,但是我們如何在運行時創建這些鍵綁定?
我認為我們需要使用new
關鍵字來初始化指針,例如buttonX_ = new JumpCommand;
,但我不確定如何創建綁定,我不明白為什么不能在運行時使用 if/else 來完成。
我對 JS 有一些經驗,而對 C++ 沒有經驗,所以如果熟悉這兩種語言的人能幫助我充實/理解這個例子中發生的事情,我將不勝感激。
我已經稍微更改了代碼,所以它更簡單。 所以,這里是命令:
using Command = void (*)();
void jump(); // TODO
void fire(); // TODO
和按鈕:
enum class Button { x, y, a, b };
bool pressed(Button); // TODO
以下是 [customizable key -> Command
] 映射如何存儲和用於處理輸入的方法。 它與您介紹的有點不同:I static_cast
Button
<-> std::size_t
to use Button
s as "keys"; Command
是“值”:
class Buttons {
std::array<Command, 4> commands{jump, jump, fire, fire};
public:
void rebind(Button button, Command command) {
commands[static_cast<std::size_t>(button)] = command;
}
void handle() {
for (std::size_t i{}; i < commands.size(); ++i)
if (pressed(static_cast<Button>(i)))
commands[i]();
}
};
因此,如果用戶想要使用x
來跳轉,則應該調用rebind(Button::x, jump)
。 原來的解決方案沒有提供統一的重新綁定,因為每個Command
都是一個單獨的字段,所以我使用了一個數組來代替。
但是為什么我們需要命令模式在運行時進行這些綁定呢?
我們沒有,這只是一種選擇,一種常見的方法。
將這個表轉換成 <commandName, key> 格式的地圖/字典,然后使用簡單的 if/else 來執行 if(isPressed(keymap["jump"])) jump() 是否足夠?
如果將字符串名稱更改為命名的整數常量,將字典更改為數組,我更喜歡您的解決方案,因為它避免了間接/虛擬 function 調用,這些調用速度較慢且template
/重載不兼容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.