Рефакторинг, если/еще логика

У меня есть класс Java с тысячей методов строки если/еще логика как это:

if (userType == "admin") {
     if (age > 12) {
          if (location == "USA") {
               // do stuff
          } else if (location == "Mexico") {
               // do something slightly different than the US case
          }
     } else if (age < 12 && age > 4) {
          if (location == "USA") {
               // do something slightly different than the age > 12 US case
          } else if (location == "Mexico") {
               // do something slightly different
          }
     }
 } else if (userType == "student") {
     if (age > 12) {
          if (location == "USA") {
               // do stuff
          } else if (location == "Mexico") {
               // do something slightly different than the US case
          }
     } else if (age < 12 && age > 4) {
          if (location == "USA") {
               // do something slightly different than the age > 12 US case
          } else if (location == "Mexico") {
               // do something slightly different
          }
     }

Как я должен осуществить рефакторинг это во что-то более managable?

35
задан David 26 May 2010 в 13:44
поделиться

14 ответов

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

Если вы действительно можете различить условие по типу пользователя, вы можете как минимум разбить тело каждого условия на отдельную функцию. Так что вы проверяете на основе типа и вызываете соответствующую функцию, специфичную для этого типа. Еще одно объектно-ориентированное решение - представить каждого пользователя как класс, а затем переопределить какой-либо метод расчета, чтобы вернуть значение, основанное на возрасте. Если вы не можете использовать классы, но можете, по крайней мере, использовать перечисления, тогда вы сможете сделать более удобный оператор switch для перечислений. Переключатели на строки появятся только в Java 7.

Меня беспокоят ситуации перекрытия (например, два типа пользователей с некоторыми общими правилами и т. Д.). Если это в конечном итоге так, вам может быть лучше представить данные в виде некоторого внешнего файла (например, таблицы), который вы будете читать и поддерживать, и ваш код по существу будет работать как драйвер, который выполняет соответствующий поиск в этих данных. установленный. Это общий подход для сложных бизнес-правил, поскольку никто не хочет поддерживать тонны кода.

2
ответ дан 27 November 2019 в 07:02
поделиться

Первое, что я сделал бы с этим кодом, - это создал бы типы Admin и Student , оба из которых наследуются от базового типа User . Эти классы должны иметь метод doStuff () , в котором вы скрываете остальную часть этой логики.

Как показывает практика, каждый раз, когда вы ловите себя на переключении типа, вы можете вместо этого использовать полиморфизм.

12
ответ дан 27 November 2019 в 07:02
поделиться

Используйте концепции ООП: Это зависит от остальной части дизайна, но, возможно, вам понадобится пользовательский интерфейс, Student , Admin интерфейсы расширяет его и UsaStudent , MexicoStudent , UsaAdmin , MexicoAdmin реализации, которые делают некоторые вещи. Сохраните экземпляр User и просто вызовите его метод doStuff .

0
ответ дан 27 November 2019 в 07:02
поделиться

Взгляните на паттерн Посетитель. Он использует полиморфизм, но является немного более гибким, так как позже легче добавлять новые случаи.

Обратной стороной является необходимость преобразования информации о состоянии в разные экземпляры. Преимущество - более чистый способ добавления поведения без изменения иерархии наследования.

1
ответ дан 27 November 2019 в 07:02
поделиться

Основываясь только на именах переменных, я предполагаю, что вы должны подклассифицировать User (или что бы это ни было, что имеет переменную userType) в AdminUser и StudentUser (и, возможно, другие) и использовать полиморфизм.

1
ответ дан 27 November 2019 в 07:02
поделиться

Если код в блоках вписывается в несколько стандартных шаблонов, я бы создал таблицу с колонками (type, location, minAge, maxAge, action), где 'action' - это перечисление, указывающее, какой из нескольких типов обработки нужно выполнить. В идеале эта таблица должна считываться из файла данных или храниться в SQL.

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

2
ответ дан 27 November 2019 в 07:02
поделиться

без дополнительной информации нет хорошего ответа

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

сначала определите пользователя, определите администратора, студента и все остальные типы пользователей, а затем позвольте полиморфизму позаботиться об остальном

.
1
ответ дан 27 November 2019 в 07:02
поделиться

Я бы, наверное, сначала проверил, можно ли параметризовать код doStuff и doSimilarStuff.

2
ответ дан 27 November 2019 в 07:02
поделиться

Первое - используйте перечисления для userType и location - тогда вы сможете использовать операторы switch (улучшает читаемость)

Второе - используйте больше методов.

Пример:

switch (userType) {
  case Admin: handleAdmin(); break;
  case Student: handleStudent(); break;
}

и позже

private void handleAdmin() {
  switch (location) {
    case USA: handleAdminInUSA(); break;
    case Mexico: handleAdminInMexico(); break;
  }
}

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

EDIT

Если кто-то заставляет вас писать на Java без перечислений (например, вас заставляют использовать Java 1.4.2), используйте 'final static' вместо перечислений или сделайте что-то вроде:

  if (isAdmin(userType)) {
    handleAdmin(location, age);
  } else if (isStudent(userType)) {
    handleStudent(location, age));
  }

//...

private void handleAdmin(String location, int age) {
  if (isUSA(location)) {
    handleAdminInUSA(age);
  } else if (isUSA(location)) {
    handleAdminInMexico(age);
  }
}

//...

private void handleAdminInUSA(int age) {
  if (isOldEnough(age)) {
    handleAdminInUSAOldEnough();
  } else if (isChild(age)) {
    handleChildishAdminInUSA(); // ;-)
  } //...
}
6
ответ дан 27 November 2019 в 07:02
поделиться

Thousands? Возможно, движок правил - это то, что вам нужно. Drools может быть жизнеспособной альтернативой.

Или шаблон Command, который инкапсулирует всю логику "сделать что-то немного другое" для каждого случая. Храните каждую команду в карте с конкатенацией возраста, местоположения и других факторов в качестве ключа. Найдите команду, выполните ее, и все готово. Красиво и чисто.

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

9
ответ дан 27 November 2019 в 07:02
поделиться

Вы можете использовать шаблон «Цепочка ответственности».

Рефакторинг операторов if-else в классы, например, с интерфейсом IUserController .

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

Кроме того, вы можете смоделировать функциональность, зависящую от местоположения, с помощью шаблона стратегии. Надеюсь, это поможет.

2
ответ дан 27 November 2019 в 07:02
поделиться

Вам действительно нужно разбить эти случаи на методы объекта. Я предполагаю, что эти строки и числа извлекаются из базы данных. Вместо того, чтобы использовать их в исходной форме в гигантской вложенной условной логике, вам нужно использовать эти фрагменты данных для создания объектов, моделирующих желаемые взаимодействия. Рассмотрим класс UserRole с подклассами StudentRole и AdminRole, класс Region с подклассами USA и Mexico и класс AgeGroup с соответствующими секционированными подклассами.

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

1
ответ дан 27 November 2019 в 07:02
поделиться

Вы должны использовать Стратегии , возможно, реализованные в перечислении, например:

enum UserType {
  ADMIN() {
    public void doStuff() {
      // do stuff the Admin way
    }
  },
  STUDENT {
    public void doStuff() {
      // do stuff the Student way
    }
  };

  public abstract void doStuff();
}

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

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

26
ответ дан 27 November 2019 в 07:02
поделиться

Вы можете сделать userType перечислением и дать ему метод, который будет выполнять все ваши действия «сделать что-то немного другое».

1
ответ дан 27 November 2019 в 07:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: