簡體   English   中英

命令模式如何解決硬連線命令/請求的問題?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM