Рефакторинг if/else логики
У меня есть класс java с методом тысячи строк if/else, например:
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
}
}
Как мне переделать это во что-то более управляемое?
Ответы
Ответ 1
Вы должны использовать Стратегии, возможно, реализованные в перечислении, например:
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, вы можете реализовать имя перечисления вручную и использовать обычное старое подклассов для реализации различных стратегий.
Ответ 2
Первое, что я сделал бы с этим кодом, это создать типы Admin
и Student
, оба из которых наследуются от базового типа User
. Эти классы должны иметь метод doStuff()
, где вы скрываете остальную часть этой логики.
Как правило, в любое время, когда вы перехватываете себя, вы можете использовать полиморфизм.
Ответ 3
Тысячи? Возможно, вам нужен механизм правил. Drools может быть жизнеспособной альтернативой.
Или шаблон команды, который инкапсулирует все "делать что-то немного отличающееся" для каждого случая. Храните каждую команду на карте с совпадением возраста, местоположения и других факторов в качестве ключа. Найдите команду, выполните ее, и все готово. Приятный и чистый.
Карту можно сохранить как конфигурацию и прочитать при запуске. Вы можете добавить новую логику, добавив новые классы и переконфигурируя.
Ответ 4
Первичные перечисления для 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;
}
}
Далее укажите идентификационный код и добавьте дополнительные методы.
ИЗМЕНИТЬ
Если кто-то заставляет вас кодировать Java без перечислений (например, вы вынуждены использовать Java 1.4.2), используйте "final static вместо enums" или выполните что-то вроде:
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(); // ;-)
} //...
}
Ответ 5
Вы можете сделать userType
a enum
и дать ему метод, который выполняет все ваши действия "делать что-то несколько другое".
Ответ 6
без дополнительной информации нет хорошего ответа
но справедливое предположение было бы следующим: используйте OO
сначала определите пользователя, определите Admin, Student и всех других типов пользователей, а затем пусть полиморфизм позаботится об остальном
Ответ 7
Основываясь только на именах переменных, я предполагаю, что вы должны подклассифицировать User
(или что бы то ни было, что имеет переменную userType
) в AdminUser
и StudentUser
(и, возможно, другие), и использовать polymorphism.
Ответ 8
Риск этого не только в том, что он неприглядный, но и в том, что он очень подвержен ошибкам. Через некоторое время вы можете столкнуться с риском совпадений в своих условиях.
Если вы действительно можете отличить условие по типу пользователя, вы можете как минимум разбить тело каждого условия на отдельную функцию. Таким образом, вы проверяете на основе типа и вызываете соответствующую функцию, специфичную для этого типа. Более OO-решение должно представлять каждого пользователя как класс, а затем переопределять некоторый метод расчета, чтобы вернуть значение в зависимости от возраста. Если вы не можете использовать классы, но можете хотя бы использовать перечисления, тогда вы сможете сделать более приятный оператор switch в перечислениях. Коммутаторы на строках будут доступны только в Java 7.
Меня беспокоят ситуации совпадений (например, два пользовательских типа с некоторыми общими правилами и т.д.). Если это произойдет, вам может быть лучше представить данные как некоторый внешний файл (например, таблицу), который вы будете читать и обслуживать, и ваш код будет по существу работать как драйвер, который выполняет соответствующий поиск в этих данных задавать. Это общий подход для сложных бизнес-правил, поскольку никто не хочет идти и поддерживать тонны кода.
Ответ 9
Я бы, наверное, сначала проверил, можете ли вы параметризовать код doStuff и doSimilarStuff.
Ответ 10
Взгляните на шаблон посетителя. Он использует полиморфизм, но немного более гибкий, поскольку легче добавлять новые случаи позже.
Недостатком является то, что вам нужно каким-то образом преобразовать информацию состояния в разные экземпляры. Преимущество - более чистый способ добавить поведение, не изменяя иерархию наследования.
Ответ 11
Вы можете использовать шаблон цепочки ответственности.
Рефакторинг if-else
операторов в классы с интерфейсом IUserController
например.
Инициализируйте свою цепочку в списке или дереве или любой подходящей структуре данных и выполните требуемые функции в этой цепочке. Вы можете использовать шаблон Builder для создания указанной структуры данных. Он похож на шаблон стратегии, но в цепочке шаблонов ответственности экземпляр в цепочке может вызывать связанные экземпляры (ы).
Кроме того, вы можете моделировать специфику местоположения, используя шаблон стратегии. Надеюсь, поможет.
Ответ 12
Если код в блоках соответствует нескольким стандартным шаблонам, я бы создал таблицу со столбцами (тип, местоположение, minAge, maxAge, действие), где "действие" - это перечисление, указывающее, какой из нескольких типов обработки делать. В идеале эта таблица будет считана из файла данных или хранится в SQL.
Затем вы можете просто выполнить поиск таблицы в коде Java, чтобы определить действие, которое требуется выполнить для пользователя.
Ответ 13
Использование концепций ООП:
Это зависит от остальной части дизайна, но, возможно, у вас должен быть интерфейс user
, Student
, Admin
интерфейсы расширяет его и UsaStudent
, MexicoStudent
, UsaAdmin
, MexicoAdmin
реализация, которая сделайте кое-что. Удерживайте экземпляр user
и просто вызовите его метод doStuff
.
Ответ 14
Вам действительно нужно разбить эти случаи на методы объектов. Я предполагаю, что эти строки и числа выводятся из базы данных. Вместо того, чтобы использовать их в своей необработанной форме в гигантской вложенной условной логике, вам нужно использовать эти данные для построения объектов, которые моделируют желаемые взаимодействия. Рассмотрим класс UserRole с подклассами StudentRole и AdminRole, класс Region с подклассами США и Мексики и класс AgeGroup с соответствующими секционированными подклассами.
Как только вы создадите эту объектно-ориентированную структуру, вы сможете использовать хорошо понятые объектно-ориентированные шаблоны проектирования, чтобы перефразировать эту логику.