Similar to Tax system, I have to write the code where some discount range is given and It is very much possible that in future I will get 10-20 more discount range.
Actual Problem statement: Shopping Cart
There are multiple types of customer(Regular, Premium) they have been given a discount based on their purchase amount. for e.x
Premium Customer
Regular Customer
I tried if-else and switch statement but situation is same. every time I have to touch the already tested method and add either switch-case condition or else-if part or say 10 more classes if polymorphism.
What is the best way to tackle such situation where there are so many condition exists and can be added in future without touching previously written code
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;
}
It would be a bit of an overkill in this case, but if the conditions get really numerous, you could use a class that expresses a function to apply on the amount, if it is within a desired range, like this:
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);
}
}