Разобраться с большим количеством if-else, switch - PullRequest
2 голосов
/ 01 сентября 2010

Как лучше всего справиться с чем-то вроде этого:

if(key==Space)
{
    switch(weapon)
    {
        case GUN:
            p->shotGun();
            break; 
        case BOW:
            p->shotBow();
            break;
    }
}
else if(key==Enter)
{
    //...
}
else if(key==Up)
{
    //...
}

Ответы [ 9 ]

10 голосов
/ 01 сентября 2010

Разделить одно измерение на два:

switch (key)
{
  case Space:
    ProcessSpaceCmd ();
    break;

  case Enter:
    ProcessEnterCmd ();
    break;

  case Up:
    ProcessUpCmd ();
    break;
}

Посмотрите, какое измерение длиннее, [key] или [weapon] и выберите самое короткое для внешнего переключателя.

9 голосов
/ 01 сентября 2010

Возможно, вы захотите подумать об использовании шаблонов Command и / или Strategy .Шаблон Command кажется подходящим для внешнего if / else, в то время как шаблон Strategy кажется подходящим для внутреннего переключателя.

  cmd = Command->GetCommand( key );
  cmd->Perform();

и в Perform для команды, связанной с клавишей пробела

  weapon = PlayState->GetCurrentWeapon();
  weapon->Fire();

Обратите внимание, что последний использует некоторый глобальный кеш / состояние для хранения текущего оружия (стратегии).

Результатом этого будет перемещение логики if / else в фабричный метод, гдеВы определяете текущую команду.Выбор какой команды выбирает одну из ветвей if / else.Хранение текущего оружия в игровом состоянии позволяет вам легко выбирать, какой метод огня активировать, таким образом, переключатель «логика», для которого выбирается оружие, перемещается в логику выбора оружия для состояния и полностью «исчезает».Каждая стратегия оружия просто знает, как выполнить свою собственную "огненную" логику.

5 голосов
/ 01 сентября 2010

Я склонен использовать выражение типа карты:

unordered_map<KEY_PRESS,ICommand> myCommands;
unordered_map<KEY_PRESS,ICommand>::const_iterator currentCommand = myCommands.find( key );
if( currendCommand != myCommands.end() ){
    currentCommand->performAction( weapon );
}

Опять же, если вы превратили оружие в объекты, а не в флаги, вы можете избавиться от перегруженного образца функций.

3 голосов
/ 01 сентября 2010

Поскольку это C ++, вы можете воспользоваться его объектно-ориентированными аспектами.Создайте интерфейс для оружия, такого как IProjectileWeapon, и создайте класс лука и оружия, который реализует этот интерфейс.Затем при вызове метода Shoot () он будет использовать правильное поведение.Это избавит от оператора переключения для оружия.Вероятно, было бы лучше иметь класс Character с оружием в качестве члена и UseWeapon () в качестве метода.Таким образом, метод UseWeapon () вызовет метод Shoot () для оружия.

Для перемещения добавьте команду перемещения в класс Character и передайте в направлении, в котором персонаж должен двигаться.

2 голосов
/ 01 сентября 2010

Другая возможность:

class Weapon {
    public:
    abstract void shoot();
}

class Gun: public Weapon {
    public:
    void shoot();
}

class Bow: public Weapon {
    public:
    void shoot();
}

if(key==Space)
{
    weapon.shoot();
}
else if(key==Enter)
{
    //...
}
else if(key==Up)
{
    //...
}

Что касается длинного череды if заявлений, это нормально. Оператор switch также будет работать, если вы работаете с целыми числами. Но наличие в каждом утверждении break может отвлекать, а забывать о нем может иметь катастрофические последствия. Это более важно, если вам нужна скорость во внутреннем цикле для критичного к производительности кода. (else if оценивает каждый по очереди, пока не найдет совпадение. switch сразу переходит к правильному варианту.)

2 голосов
/ 01 сентября 2010

Или, если вы действительно хотите избавиться от if-elses, вы можете использовать некоторую форму отображения между кодами клавиш и методами.В зависимости от того, являются ли коды клавиш последовательными, это может быть карта или просто массив, проиндексированный кодом ключа, например:

KeyMethods[key]();

1 голос
/ 01 сентября 2010

Как насчет уровней вложенных операторов switch? Для лучшей читабельности я бы также реорганизовал переключатели второго уровня в отдельные функции.

0 голосов
/ 01 сентября 2010

Сделайте свой класс оружия виртуальным.

0 голосов
/ 01 сентября 2010

Я бы посоветовал хотя бы разделить содержимое каждого If на его собственную функцию.

if(key == Space)
    FureCurrentWeapon();
else if(key == Enter)
    Activate();
else if(key == Up)
    WalkForwards();

Альтернативой, которую я использовал ранее, является создание базового класса, который может быть получен из каждого класса.который получает ввод с клавиатуры.Сохраняйте список классов, производных от него, затем итерируйте этот список каждый раз, когда вы получаете ввод, передавая нажатую клавишу и позволяя им обрабатывать его, если они захотят.

...