除了 Switch 语句和多态性之外,如何替换这么多 if else 条件
How to replace so many if else condition, apart from Switch statement and Polymorphism
类似于税收系统,我必须编写代码,其中给出了一些折扣范围,将来我很可能会获得 10-20 个折扣范围。
实际问题陈述:购物车
有多种类型的客户(普通客户、高级客户)根据他们的购买金额为他们提供了折扣。 e.x
高级客户
- 0-5000 美元 0%
- 5000-10000 美元 10%
- 10000 - 超过 20%
普通客户
- 0-4000 美元 10%
- $4000 - $8000 15%
- 8000 美元 - 12000 美元 20%
- 12000 美元 - 超过 25%
我尝试了 if-else 和 switch 语句,但情况是一样的。每次我必须触摸已经测试过的方法并添加 switch-case 条件或 else-if 部分或者说 10 更多 类 if 多态性。
解决这种情况的最佳方法是什么存在如此多的条件并且可以在将来添加而不触及以前编写的代码
public class PremiumCustomerBillingStrategy extends BillingStrategy {
@Override
public double calculateFinalBill(double actualBillAmt) {
double finalBillAmt;
if (actualBillAmt <= 4000) {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else if (actualBillAmt > 4000 && actualBillAmt <= 8000) {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt - 4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else if (actualBillAmt > 8000 && actualBillAmt <= 12000) {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt - 8000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt - 12000, PremiumDiscountEnum.ABOVE_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
return finalBillAmt;
}
在这种情况下有点矫枉过正,但如果条件真的很多,您可以使用 class 表示一个函数来应用金额,如果它在所需的范围内范围,像这样:
public class Bills {
class BillFor {
public final double rangeFrom;
public final double rangeTo;
public final Function<Double, Double> calculation;
BillFor(double rangeFrom, double rangeTo, Function<Double, Double> calculation) {
this.rangeFrom = rangeFrom;
this.rangeTo = rangeTo;
this.calculation = calculation;
}
}
private final static float MIN = 0f;
private final static float MAX = Long.MAX_VALUE;
private List<BillFor> billings = List.of(new BillFor(MIN, 4000, i -> i - getDiscountedAmount(i, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount)),
/*etc*/
new BillFor(12000f, MAX, i -> i - 0 /* big expression actually*/));
public double calculateFinalBill(double actualBillAmt) {
return billings.stream()
.filter(b -> actualBillAmt > b.rangeFrom && b.rangeTo <= actualBillAmt)
.findAny()
.map(b -> b.calculation.apply(actualBillAmt))
.orElseThrow(IllegalStateException::new);
}
}
好吧,你是说你没有编写一些代码,而是引入了新功能,这既不是好的做法,也不可能。所以,想一想你的测试仍然通过或者在你添加一堆行后覆盖率没有减少的情况。
但这是一个真正的问题。因此,您可以查找以下问题:
重复计算可以移出条件。在这种情况下,移出进行最终账单金额计算,即 finalBillamount = actualBillAmt - 折扣应在上述所有条件之外。
折扣的计算可能是此处代码条件的抽象。因此,将此代码模块化并转移到另一种为您计算折扣的方法。这样你就永远不会修改这组代码中的任何东西。
例如:
@Override
public double calculateFinalBill(double actualBillAmt) {
return actualBillAmt- calculateDiscount(finalBillAmt );
}
public double calculateDiscount(double actualBillAmt) {
double discount;
if (actualBillAmt <= 4000) {
discount = getDiscountedAmount(actualBillAmt, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else if (actualBillAmt > 4000 && actualBillAmt <= 8000) {
discount= getDiscountAmountUpto8K(actualBillAmt );
} else if (actualBillAmt > 8000 && actualBillAmt <= 12000) {
discount= getDiscountAmountUpto12K(actualBillAmt );
} else {
discount = getDiscountAbove16k(actualBillAmt);
}
return discount;
}
public getDiscountAmountUpto8K(double actualBillAmt ){
return getDiscountedAmount(actualBillAmt - 4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000,PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
public getDiscountAmountUpto12K(double actualBillAmt ){
return getDiscountedAmount(actualBillAmt - 8000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
public getDiscountAbove16k(double actualBillAmt ){
return getDiscountedAmount(actualBillAmt - 12000, PremiumDiscountEnum.ABOVE_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
现在您可以观察到折扣计算已从原始方法中移出。因此,您永远不需要为该方法修改测试用例。但这并不意味着您不会为新条件或添加的任何新方法编写测试用例。
类似于税收系统,我必须编写代码,其中给出了一些折扣范围,将来我很可能会获得 10-20 个折扣范围。
实际问题陈述:购物车
有多种类型的客户(普通客户、高级客户)根据他们的购买金额为他们提供了折扣。 e.x
高级客户
- 0-5000 美元 0%
- 5000-10000 美元 10%
- 10000 - 超过 20%
普通客户
- 0-4000 美元 10%
- $4000 - $8000 15%
- 8000 美元 - 12000 美元 20%
- 12000 美元 - 超过 25%
我尝试了 if-else 和 switch 语句,但情况是一样的。每次我必须触摸已经测试过的方法并添加 switch-case 条件或 else-if 部分或者说 10 更多 类 if 多态性。
解决这种情况的最佳方法是什么存在如此多的条件并且可以在将来添加而不触及以前编写的代码
public class PremiumCustomerBillingStrategy extends BillingStrategy {
@Override
public double calculateFinalBill(double actualBillAmt) {
double finalBillAmt;
if (actualBillAmt <= 4000) {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else if (actualBillAmt > 4000 && actualBillAmt <= 8000) {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt - 4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else if (actualBillAmt > 8000 && actualBillAmt <= 12000) {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt - 8000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else {
finalBillAmt = actualBillAmt - getDiscountedAmount(actualBillAmt - 12000, PremiumDiscountEnum.ABOVE_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
return finalBillAmt;
}
在这种情况下有点矫枉过正,但如果条件真的很多,您可以使用 class 表示一个函数来应用金额,如果它在所需的范围内范围,像这样:
public class Bills {
class BillFor {
public final double rangeFrom;
public final double rangeTo;
public final Function<Double, Double> calculation;
BillFor(double rangeFrom, double rangeTo, Function<Double, Double> calculation) {
this.rangeFrom = rangeFrom;
this.rangeTo = rangeTo;
this.calculation = calculation;
}
}
private final static float MIN = 0f;
private final static float MAX = Long.MAX_VALUE;
private List<BillFor> billings = List.of(new BillFor(MIN, 4000, i -> i - getDiscountedAmount(i, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount)),
/*etc*/
new BillFor(12000f, MAX, i -> i - 0 /* big expression actually*/));
public double calculateFinalBill(double actualBillAmt) {
return billings.stream()
.filter(b -> actualBillAmt > b.rangeFrom && b.rangeTo <= actualBillAmt)
.findAny()
.map(b -> b.calculation.apply(actualBillAmt))
.orElseThrow(IllegalStateException::new);
}
}
好吧,你是说你没有编写一些代码,而是引入了新功能,这既不是好的做法,也不可能。所以,想一想你的测试仍然通过或者在你添加一堆行后覆盖率没有减少的情况。
但这是一个真正的问题。因此,您可以查找以下问题:
重复计算可以移出条件。在这种情况下,移出进行最终账单金额计算,即 finalBillamount = actualBillAmt - 折扣应在上述所有条件之外。
折扣的计算可能是此处代码条件的抽象。因此,将此代码模块化并转移到另一种为您计算折扣的方法。这样你就永远不会修改这组代码中的任何东西。
例如:
@Override
public double calculateFinalBill(double actualBillAmt) {
return actualBillAmt- calculateDiscount(finalBillAmt );
}
public double calculateDiscount(double actualBillAmt) {
double discount;
if (actualBillAmt <= 4000) {
discount = getDiscountedAmount(actualBillAmt, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
} else if (actualBillAmt > 4000 && actualBillAmt <= 8000) {
discount= getDiscountAmountUpto8K(actualBillAmt );
} else if (actualBillAmt > 8000 && actualBillAmt <= 12000) {
discount= getDiscountAmountUpto12K(actualBillAmt );
} else {
discount = getDiscountAbove16k(actualBillAmt);
}
return discount;
}
public getDiscountAmountUpto8K(double actualBillAmt ){
return getDiscountedAmount(actualBillAmt - 4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000,PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
public getDiscountAmountUpto12K(double actualBillAmt ){
return getDiscountedAmount(actualBillAmt - 8000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
public getDiscountAbove16k(double actualBillAmt ){
return getDiscountedAmount(actualBillAmt - 12000, PremiumDiscountEnum.ABOVE_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_TWELVE_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_EIGHT_THOUSAND.discount)
- getDiscountedAmount(4000, PremiumDiscountEnum.BELOW_FOUR_THOUSAND.discount);
}
现在您可以观察到折扣计算已从原始方法中移出。因此,您永远不需要为该方法修改测试用例。但这并不意味着您不会为新条件或添加的任何新方法编写测试用例。