如何简化复杂的业务“IF”逻辑?

时间:2009-10-22 13:34:39

标签: refactoring business-logic

处理复杂业务逻辑的好方法是什么,从一开始就需要许多嵌套的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) {
    }
}

即使您将IF替换为开关/外壳,它仍然太复杂了。 有什么方法可以使它具有可读性,可维护性,可测试性和易于理解性?

9 个答案:

答案 0 :(得分:12)

好问题。 “条件复杂性”是一种代码气味。 Polymorphism是你的朋友。

  

条件逻辑在其初期是无辜的,当它易于理解并包含在其中时   几行代码。不幸的是,它很少老化。您实现了几个新功能   突然你的条件逻辑变得复杂和膨胀。 [Joshua Kerevsky:重构模式]

如果要学习使用Guard Clauses,你可以做的最简单的事情就是避免嵌套if。

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 ConditionalReplace Conditional with VisitorReverse Conditional

答案 1 :(得分:9)

Specification pattern可能就是你要找的东西。

摘要:

  

在计算机编程中,规范模式是一种特定的软件设计模式,通过使用布尔逻辑将业务逻辑链接在一起,可以重新构建业务逻辑。

答案 2 :(得分:4)

我会写一个通用的状态机,它以要比较的事物列表为基础。

答案 3 :(得分:2)

面向对象的方法是让多个折扣类实现一个通用接口:

dicsount.apply(order)

确定订单是否符合折扣类别中的折扣的逻辑。

答案 4 :(得分:1)

使用guard clauses可能有所帮助。

答案 5 :(得分:1)

FWIW,我已成功地使用Hamcrest来做这类事情。我相信你可以说它实现了规范模式,@ Arnis谈过。

答案 6 :(得分:1)

你应该看到

<强> Clean Code Talks - Inheritance, Polymorphism, & Testing
作者:MiškoHevery

Google Tech Talks 2008年11月20日

摘要

您的代码是否包含if语句?切换声明?你在不同的地方有相同的开关声明吗?当您进行更改时,您是否发现自己在几个地方进行相同的更改if / switch?你有没有忘记一个?

本演讲将讨论使用面向对象技术去除许多条件的方法。结果是更清晰,更紧凑,设计更好的代码,更易于测试,理解和维护。

答案 7 :(得分:0)

我的第一个想法是,这是不可测试的,这导致我找到一个解决方案,以使其可测试。

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

然后,您可以将每个嵌套的if语句作为单独的调用。通过这种方式,您可以单独测试它们,当您测试大组时,您知道每个组都可以正常工作。

答案 8 :(得分:0)

制作检查特定案例的方法。

bool IsValueNormalAndRequiresCoopon(折扣优惠){...}

bool IsValueNormalAndRequiresCoupon(折扣折扣){...}

一旦开始这样做,就会更容易看出在哪里可以抽象出选择之间的共同逻辑。然后你可以从那里去。

对于复杂的决策,我经常会得到一个处理可能状态的类。