Рефакторинг оператора if для использования соответствующего шаблона - PullRequest
2 голосов
/ 12 ноября 2010

У меня есть перечисление с некоторыми состояниями:

enum State
{
    A,
    B,
    C,
    D
}

и объект, который имеет соответствующее состояние:

class MyObject
{
    State state;
}

Мне нужно написать алгоритм, который принимает два MyObjectэкземпляры и делают что-то в зависимости от конкретных состояний этих экземпляров:

void doWork(MyObject o1, MyObject o2)
{
     if (o1.state == A && o2.state == A)
     {
          // do something
     }
     else if (o1.state == A && o2.state == B)
     {}
     // etc for all combinations...

}

Очевидно, что у этого подхода много проблем, и я хотел бы изменить его, чтобы в идеале избавиться от оператора if / else.

Есть ли образец для такого требования?

Спасибо

Ответы [ 6 ]

3 голосов
/ 12 ноября 2010

То, что вы могли бы сделать, хотя я не уверен, что это будет намного лучше, - это некая матрица всех возможных комбинаций двух state значений;затем вы можете использовать o1.state и o2.state в качестве индексов для этой матрицы.

В этой матрице вы можете хранить разные вещи:

  • уникальное значение, которое вы можете использовать в качестверазличающее значение для switch блока, который заменит ваши if .. else if .. else блоки - на самом деле не так уж и много улучшений.

Или ваша матрица может содержать ...

  • командные объекты.(Посмотрите шаблон Command .)

Если вы действительно хотите избавиться от операторов if, этот второй вариант может быть лучшим;однако учтите, что ваш код больше не будет располагаться близко друг к другу в одном месте, как в случае с блоками if / switch, а будет распределен между несколькими различными объектами / классами команд.

// forgive my syntax errors etc., my Java has definitely gone a little rusty!

interface WorkCommand {
    public abstract void run(MyObject o1, MyObject o2);
}

...

Map<Pair<State,State>, WorkCommand> commands;
// ^ pseudo-code type for your command look-up map; type Pair<X,Y> doesn't exist,
//   so replace this with something sensible!

void doWork(MyObject o1, MyObject o2)
{
    WorkCommand worker = commands.get(new Pair<State,State>(o1, o2));
    worker.run(o1, o2);
}
2 голосов
/ 12 ноября 2010

Да, это называется ... шаблон состояния . Важно иметь только одно состояние, для которого можно определить поведение, т. Е. Вам может потребоваться объединить ваши object1.state и object2.state в мета-состояние. Зарегистрируйте это мета-состояние с помощью statecontext, чтобы при изменении Myobject.state мета-состояние обновлялось.

interface MyObjectStates {
  void doWork(MyObject o1, MyObject o2);
}

class MyObjectStatesAA implements MyObjectStates {
  void doWork(MyObject o1, MyObject o2) {
    // do dowork for both states A
  }

class MyObjectStatesBB implements MyObjectStates {
  void doWork(MyObject o1, MyObject o2) {
    // do dowork for both states B
  }

// etc

Затем вам нужно держать один объект MyObjectStates в тексте состояния и обновлять его при изменении MyObject.state. Возможно, вам даже удастся полностью удалить перечисление состояния. Если такой подход кажется вам интересным, дайте мне записку, и я уточню, если хотите.

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

2 голосов
/ 12 ноября 2010

Один из способов структурировать это состоит в том, что в вашем перечислении может быть абстрактный метод, который каждый элемент будет реализовывать:

enum State
{
    A{
      public void void doSomeWork(State state){
        switch(state){
           case A:
           case B:
           ...
        }
      }
    },
    B,
    C,
    D

    abstract void doSomeWork(State state);
}

Тогда ваш метод может выглядеть как

void doWork(MyObject o1, MyObject o2){
   o1.state.doSomeWork(o2.state);
}
1 голос
/ 12 ноября 2010

Я бы сделал это, вероятно, по крайней мере более читабельно

void doWork(MyObject o1, MyObject o2) {
    switch (o1.state) {
        case A: {
            switch (o2.state) {
                case A: {

                    break;
                }
                case B: {

                    break;
                }

            }
            break;
        }
    }
}
0 голосов
/ 12 ноября 2010

ОО-решение вашей проблемы - шаблон состояния. Он включает в себя инкапсуляцию состояний, чередующихся с состоянием, с объектом состояния. Вы можете найти больше информации о модели здесь и здесь . Кстати, я бы настоятельно рекомендовал купить эту книгу, если у вас ее еще нет.

Ура!

0 голосов
/ 12 ноября 2010

, используя только две комбинации состояний, возможно, самый быстрый для реализации и понимания вложенный переключатель:

switch (o1.state) {
 case X: switch(o2.state) { }
 //..etc
}

Если порядок дел не имеет значения для определенных комбинаций, вы можете поменять местами o1 и o2 в этих случаях, а затем сбросить их в switch (и избежать дублирования кода). Кроме того, для всех комбинаций наблюдений, имеющих одинаковое поведение, вы можете воспользоваться преимуществом отклоняющегося поведения.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...