2009-10-22 4 views
13

Каковы хорошие способы обработки сложной бизнес-логики, которая с первого взгляда требует множества вложенных операторов if?Как упростить сложную бизнес-логику «ЕСЛИ»?

Пример:

Дисконтный купон. может быть:

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

2а) Нормальная скидка
2b) Прогрессивная скидка

3a) Требуется купон
3b доступа) Не требуется купон

доступа

4a) Применяется только для клиентов, которые уже купили до
4b) Применимо к любому клиенту

5а) Применительно к заказчику только из стран (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) { 
    } 
} 

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

ответ

4

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

9

Specification pattern может быть то, что вы ищете.

Резюме:

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

2

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

dicsount.apply(order) 

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

+0

Это не разные классы скидок. Это один класс скидок со всеми этими 1-5 вещами, как возможными свойствами. – Zelid

+0

Я предлагаю вам изменить это, чтобы у вас было несколько классов скидок. –

+0

Хорошо, как тогда указать, что скидка должна быть 1a, 2b, 3a, 4b одновременно? – Zelid

0

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

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

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

+0

Это похоже на то, что оно реализовано сейчас, но CallFunctionX() требует много IFs внутри него и так далее ... – Zelid

+0

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

0

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

BOOL IsValueNormalAndRequiresCoopon (скидка скидка) {...}

BOOL IsValueNormalAndRequiresCoupon (скидка скидка) {...}

и т.д.

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

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

+1

Создание ~ 32 таких функций для всех комбинаций свойств тоже не так хорошо – Zelid

+0

К тому времени, как вы их заработаете, вы должны начать видеть возможности для проведения общих проверок и повторного факторинга кода для более простой структуры. Идея состоит в том, чтобы удалить некоторые деревья, чтобы вы могли начать просматривать лес. – ElGringoGrande

1

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

+0

Но разве это не нарушает принцип единственного выхода? –

+0

Определенно. Хотя я не знаю, какое значение имеет это правило. Я, конечно, не смущаюсь, чтобы следовать за ним. Даже если вы подписались на него, я думаю, что оговорки о защите будут приемлемым исключением. –

+1

Оговорки охраны потребуют одной новой функции за каждую возможную комбинацию из 5 различных скидочных купонов (объем/процент, нормальный/прогрессивный, RequireCoupon/DoNotRequireCoupon, ...), который также не очень хорош – Zelid

11

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

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

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

double getPayAmount() { 
if (_isDead) return deadAmount(); 
if (_isSeparated) return separatedAmount(); 
if (_isRetired) return retiredAmount(); 
return normalPayAmount(); 
}; 

Другая вещь, которую я нашел, упрощает вещи довольно хорошо, и что делает ваш код самодокументируемыми, является Consolidating conditionals.

double disabilityAmount() { 
    if (isNotEligableForDisability()) return 0; 
    // compute the disability amount 

Другие ценные refactoring методы, связанные с условными выражениями включают Decompose Conditional, Replace Conditional with Visitor и Reverse Conditional.

+0

+1 - Мне нравится, как вы дали различные варианты. –

+1

Похоже, что решение Guard Clauses будет работать только в том случае, если сравнение атрибутов самоисключительно (_isDead, _isSeparated, isRetired) невозможно использовать это решение для приведенного примера, потому что Скидочный купон может быть как «1a», так и дисконтом стоимости, 2a) Нормальная скидка, 3b) Не требуется купон на доступ, 4a) Применяется только для клиента, который уже купил ранее и 5a) Применяется только для клиентов только из стран (X, Y, ...) " – Zelid

+0

или добавить около 32 таких операторов возврата: _isValueAndNoramAndAcees() _isValueAndProgressiveAndNotRequireAccessAndRequireCountires() .... все другие комбинации в IF и и в функции – Zelid

0

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

1

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

Clean Code Talks - Inheritance, Polymorphism, & Testing
по Miško Hevery

Google Tech Talks 20 ноября 2008

РЕФЕРАТ

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

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

+0

Спасибо, будут читать звуки очень полезны – Zelid

Смежные вопросы