Как упростить сложную бизнес-логику “ЕСЛИ”?

StackOverflow https://stackoverflow.com/questions/1607252

  •  05-07-2019
  •  | 
  •  

Вопрос

Каковы хорошие способы обработки сложной бизнес-логики, которая на первый взгляд требует множества вложенных операторов 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 на switch / case, это все равно будет слишком сложно.Каковы способы сделать его читабельным, обслуживаемым, более тестируемым и простым для понимания?

Это было полезно?

Решение

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

Другие советы

Хороший вопрос."Условная сложность" - это запах кода. Полиморфизм это твой друг.

Условная логика невинна в своем зачаточном состоянии, когда она проста для понимания и содержится в пределах нескольких строк кода.К сожалению, он редко хорошо стареет.Вы внедряете несколько новых функций и внезапно ваша условная логика становится сложной и расширяющейся.[Джошуа Керевски:Рефакторинг к шаблонам]

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

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

Другие ценные рефакторинг методы , связанные с условными выражениями , включают Разложить Условное, Заменить Условное на Посетитель, и Обратное Условие.

Шаблон спецификации возможно, это то, что вы ищете.

Краткие сведения:

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

Объектно ориентированный способ сделать это состоит в том, чтобы иметь несколько классов скидок, реализующих общий интерфейс:

dicsount.apply(order)

Поместите логику для определения того, подпадает ли заказ под действие скидки, в классы скидок.

Используя охранные положения могло бы кому-то помочь.

FWIW, я использовал Хэмкрест очень успешно для такого рода вещей.Я полагаю, вы могли бы сказать, что он реализует Шаблон спецификации, о котором говорил @Arnis.

Вы действительно должны увидеть

Беседы о чистом коде - Наследование, полиморфизм и тестирование
автор Мишко Хевери

Технические доклады Google 20 ноября 2008

АБСТРАКТНЫЙ

Полон ли ваш код операторов if?Переключать операторы?Есть ли у вас один и тот же оператор switch в разных местах?Когда вы вносите изменения, обнаруживаете ли вы, что вносите одно и то же изменение в одно и то же if / switch в нескольких местах?Вы когда-нибудь забывали об одном из них?

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

Моя первая мысль заключается в том, что это не поддается проверке, что приводит меня к решению, чтобы сделать его поддающимся проверке.

if (discount.isPercentage) {
  callFunctionOne(...);
} else if (discount.isValue) {
  callFunctionThree(...);
} else if (discount.isXXX) {
  callFunctionTwo(...);
}

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

Создайте методы, которые проверяют наличие конкретного случая.

bool - это значение normalandrequirescoopon(Скидка со скидкой){...}

bool - значение normalandrequirescoupon(Скидка со скидкой){...}

и т.д.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top