Как упростить сложную бизнес-логику «ЕСЛИ»? - PullRequest
14 голосов
/ 22 октября 2009

Каковы хорошие способы обработки сложной бизнес-логики, которая с первого взгляда требует много вложенных операторов if?

Пример:

Купон на скидку. может быть:

1а) Значение скидки
1b) Процентная скидка

2а) Обычная скидка
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) {
    }
}

Даже если вы замените IF для переключения / дела, это все равно слишком сложно. Как можно сделать его читабельным, понятным, более тестируемым и простым для понимания?

Ответы [ 9 ]

12 голосов
/ 22 октября 2009

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

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

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

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

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

9 голосов
/ 22 октября 2009

Спецификация может быть тем, что вы ищете.

Резюме:

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

4 голосов
/ 22 октября 2009

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

2 голосов
/ 22 октября 2009

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

dicsount.apply(order)

Укажите логику для определения того, соответствует ли заказ критериям скидки в пределах классов скидок.

1 голос
/ 25 марта 2011

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

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

Google Tech Talks 20 ноября 2008 года

РЕЗЮМЕ

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

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

1 голос
/ 22 октября 2009

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

1 голос
/ 22 октября 2009

Использование охранных предложений может помочь некоторым.

0 голосов
/ 22 октября 2009

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

bool IsValueNormalAndRequiresCoopon (Скидка со скидкой) {...}

bool IsValueNormalAndRequiresCoupon (Скидка со скидкой) {...}

и т.д.

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

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

0 голосов
/ 22 октября 2009

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

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

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

...