处理复杂业务逻辑的好方法是什么,从一开始就需要许多嵌套的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替换为开关/外壳,它仍然太复杂了。 有什么方法可以使它具有可读性,可维护性,可测试性和易于理解性?
答案 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 Conditional,Replace Conditional with Visitor和Reverse Conditional。
答案 1 :(得分:9)
答案 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(折扣折扣){...}
等
一旦开始这样做,就会更容易看出在哪里可以抽象出选择之间的共同逻辑。然后你可以从那里去。
对于复杂的决策,我经常会得到一个处理可能状态的类。