Ответ 1
Я бы написал общую машину состояний, которая питается списками вещей для сравнения.
Каковы хорошие способы обработки сложной бизнес-логики, которая с первого взгляда требует множества вложенных операторов if?
Пример:
Скидка. может быть:
1a) Скидка стоимости
1b) Процентная скидка
2a) Нормальная скидка
2b) Прогрессивная скидка
3a) Требуется купон доступа
3b) Не требуется купон доступа
4a) Применяется только к клиенту, который уже купил ранее 4b) Применяется к любому клиенту
5a) Применяется только для клиентов из стран (X, Y,...)
Это требует еще более сложного кода:
if (discount.isPercentage) {
if (discount.isNormal) {
if (discount.requiresAccessCoupon) {
} else {
}
} else if (discount.isProgressive) {
if (discount.requiresAccessCoupon) {
} else {
}
}
} else if (discount.isValue) {
if (discount.isNormal) {
if (discount.requiresAccessCoupon) {
} else {
}
} else if (discount.isProgressive) {
if (discount.requiresAccessCoupon) {
} else {
}
}
} else if (discount.isXXX) {
if (discount.isNormal) {
} else if (discount.isProgressive) {
}
}
Даже если вы замените IFs на переключатель /case, это все еще слишком сложно. Каковы способы сделать его читабельным, ремонтопригодным, более проверяемым и понятным?
Я бы написал общую машину состояний, которая питается списками вещей для сравнения.
Условная логика невинна в зачаточном состоянии, когда ее просто понять и несколько строк кода. К сожалению, он редко стареет. Вы реализуете несколько новых функций и внезапно ваша условная логика становится сложной и экспансивной. [Джошуа Керевский: Рефакторинг для образцов]
Одна из самых простых вещей, которые вы можете сделать, чтобы избежать вложенных блоков, чтобы научиться использовать Оговорки о защите.
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};
Другая вещь, которую я нашел, упрощает все, что делает ваш код самодокументированным, Консолидация условностей.
double disabilityAmount() {
if (isNotEligableForDisability()) return 0;
// compute the disability amount
Другие ценные методы refactoring, связанные с условными выражениями, включают Decompose Conditional, Заменить условный с посетителем и Обратный условный.
Спецификация шаблона может быть тем, что вы ищете.
Сводка:
В компьютерном программировании шаблон спецификации представляет собой конкретный шаблон разработки программного обеспечения, посредством чего бизнес-логику можно объединить, объединив бизнес-логику, используя логическую логику.
Объектно-ориентированный способ сделать это состоит в том, чтобы иметь несколько классов скидок, реализующих общий интерфейс:
dicsount.apply(order)
Поместите логику для определения того, соответствует ли заказ для скидки в классах скидок.
Использование сторожевых предложений может помочь некоторым.
Вы действительно должны увидеть
Чистые кодовые разговоры - наследование, полиморфизм и тестирование
by Miško Hevery
Google Tech Talks 20 ноября 2008 г.
РЕЗЮМЕ
Является ли ваш код полным для операторов if? Выключить инструкции? У вас есть одно и то же предложение switch в разных местах? Когда вы вносите изменения, вы обнаружите, что делаете одно и то же изменение с тем же, если /switch в нескольких местах? Вы когда-нибудь забыли его?
В этом обсуждении будут обсуждаться подходы к использованию объектно-ориентированных методов для удаления многих из этих условностей. В результате получается более чистый, более прочный, более совершенный код, который легче тестировать, понимать и поддерживать.
Моя первая мысль заключается в том, что это невозможно проверить, что приводит меня к решению, чтобы проверить его.
if (discount.isPercentage) {
callFunctionOne(...);
} else if (discount.isValue) {
callFunctionThree(...);
} else if (discount.isXXX) {
callFunctionTwo(...);
}
Тогда вы можете иметь каждый вложенный оператор if как отдельный вызов. Таким образом, вы можете протестировать их индивидуально, и когда вы проверите большую группу, вы знаете, что каждый из них работает.
Сделайте методы, которые проверяют конкретный случай.
bool IsValueNormalAndRequiresCoopon (скидка скидка) {...}
bool IsValueNormalAndRequiresCoupon (скидка скидка) {...}
и т.д.
Как только вы начнете это делать, становится легче увидеть, где вы можете абстрагироваться от общей логики между выборами. Вы можете пойти оттуда.
Для сложных решений я часто получаю класс, который обрабатывает возможные состояния.
FWIW, я использовал Hamcrest очень успешно для такого рода вещей. Я думаю, вы могли бы сказать, что он реализует шаблон спецификации, о котором говорил @Arnis.