3
\$\begingroup\$

I am new in Application Design. I have use-case as below enter *emphasized text*image description here

As per the above story I have implemented code as below without any Modularity, Extensible and Maintainable. Could someone share the thoughts how to design below bad code to production grade application ?

Based on this story context, there could be other stories which could build-upon in later. Example: Introduce new customer types like Gold, Diamond, Platinum, etc and their discounts slabs.

I really appreciate if some one have a look at the code below and let me know the how to design before creating data members, Interfaces and classes of improvements.

public class ShoppingCartDiscount { public static void main(String args[]) { System.out.println("Price for the premium customer: " + calculatePrice("Premium", 20000)); System.out.println("Price for the regular customer: " + calculatePrice("Regular", 8000)); } static float calculatePrice(String customerType, float purchaseAmount) { float total = 0; if (customerType.equalsIgnoreCase("Regular") { if (purchaseAmount > 5000 && purchaseAmount <= 10000) { float firstSlab = purchaseAmount - 5000; firstSlab = firstSlab - (float) (firstSlab * 0.1); total = 5000 + firstSlab; } else if (purchaseAmount > 10000) { float secondSlab = purchaseAmount - 10000; secondSlab = secondSlab - (float) (secondSlab * 0.2); float firstSlab = 10000 - 5000; firstSlab = firstSlab - (float) (firstSlab * 0.1); System.out.println("firstSlab:" + firstSlab); total = 5000 + firstSlab; total = total + secondSlab; } else if (purchaseAmount <= 5000 && purchaseAmount >= 0) { total = purchaseAmount; } else { return purchaseAmount; } } else if (customerType.equalsIgnoreCase("premium")) { if (purchaseAmount <= 4000) { total = purchaseAmount - (float) (purchaseAmount * 0.1); } if (purchaseAmount > 4000 && purchaseAmount <= 8000) { float secondSlab = purchaseAmount - 4000; secondSlab = secondSlab - (float) (secondSlab * 0.15); float firstSlab = 8000 - 4000; total = firstSlab - (float) (firstSlab * 0.1); total = total + secondSlab; } if (purchaseAmount > 8000 && purchaseAmount <= 12000) { float thirdSlab = purchaseAmount - 8000; thirdSlab = thirdSlab - (float) (thirdSlab * 0.20); float secondSlab = 8000 - 4000; secondSlab = secondSlab - (float) (secondSlab * 0.15); float firstSlab = 8000 - 4000; total = firstSlab - (float) (firstSlab * 0.1); total = total + secondSlab + thirdSlab; } if (purchaseAmount > 12000) { float fourthSlab = purchaseAmount - 12000; fourthSlab = fourthSlab - (float) (fourthSlab * 0.30); float thirdSlab = 8000 - 4000; thirdSlab = thirdSlab - (float) (thirdSlab * 0.20); float secondSlab = 8000 - 4000; secondSlab = secondSlab - (float) (secondSlab * 0.15); float firstSlab = 8000 - 4000; total = firstSlab - (float) (firstSlab * 0.1); total = total + secondSlab + thirdSlab + fourthSlab; } } return total; } } 
\$\endgroup\$
0

    1 Answer 1

    2
    \$\begingroup\$

    I separated the calculatePrice method into calculatePremiumPrice and calculateRegularPrice methods. This made the code easier to visually inspect.

    I also made the two blocks of if statements consistent, starting with the lower discounts and working my way up to the larger discounts. Again, this makes the code easier to visually inspect.

    I was more measured in my use of blank lines. There's no need to put a blank line after every statement. Use blank lines to separate logical concepts within a method.

    If the method code fits on one screen, it's easier to visually inspect.

    Finally, I ran all the test cases and formatted the output into currency.

    import java.text.NumberFormat; public class ShoppingCartDiscount { static NumberFormat formatter = NumberFormat.getCurrencyInstance(); public static void main(String args[]) { System.out.println("Price for the regular customer: " + formatter.format(calculatePrice("Regular", 5000))); System.out.println("Price for the regular customer: " + formatter.format(calculatePrice("Regular", 10000))); System.out.println("Price for the regular customer: " + formatter.format(calculatePrice("Regular", 15000))); System.out.println(); System.out.println("Price for the premium customer: " + formatter.format(calculatePrice("Premium", 4000))); System.out.println("Price for the premium customer: " + formatter.format(calculatePrice("Premium", 8000))); System.out.println("Price for the premium customer: " + formatter.format(calculatePrice("Premium", 12000))); System.out.println("Price for the premium customer: " + formatter.format(calculatePrice("Premium", 20000))); } static float calculatePrice(String customerType, float purchaseAmount) { float total = 0f; if (customerType.equalsIgnoreCase("Regular")) { total = calculateRegularPrice(purchaseAmount); } else if (customerType.equalsIgnoreCase("Premium")) { total = calculatePremiumPrice(purchaseAmount); } return total; } static float calculateRegularPrice(float purchaseAmount) { float total = 0f; if (purchaseAmount <= 5000) { total = purchaseAmount; } else if (purchaseAmount > 5000 && purchaseAmount <= 10000) { float firstSlab = purchaseAmount - 5000; firstSlab = firstSlab - (float) (firstSlab * 0.1); total = 5000 + firstSlab; } else { float secondSlab = purchaseAmount - 10000; secondSlab = secondSlab - (float) (secondSlab * 0.2); float firstSlab = 10000 - 5000; firstSlab = firstSlab - (float) (firstSlab * 0.1); total = 5000 + firstSlab; total = total + secondSlab; } return total; } static float calculatePremiumPrice(float purchaseAmount) { float total = 0f; if (purchaseAmount <= 4000) { total = purchaseAmount - (float) (purchaseAmount * 0.1); } else if (purchaseAmount > 4000 && purchaseAmount <= 8000) { float secondSlab = purchaseAmount - 4000; secondSlab = secondSlab - (float) (secondSlab * 0.15); float firstSlab = 8000 - 4000; total = firstSlab - (float) (firstSlab * 0.1); total = total + secondSlab; } else if (purchaseAmount > 8000 && purchaseAmount <= 12000) { float thirdSlab = purchaseAmount - 8000; thirdSlab = thirdSlab - (float) (thirdSlab * 0.20); float secondSlab = 8000 - 4000; secondSlab = secondSlab - (float) (secondSlab * 0.15); float firstSlab = 8000 - 4000; total = firstSlab - (float) (firstSlab * 0.1); total = total + secondSlab + thirdSlab; } else { float fourthSlab = purchaseAmount - 12000; fourthSlab = fourthSlab - (float) (fourthSlab * 0.30); float thirdSlab = 8000 - 4000; thirdSlab = thirdSlab - (float) (thirdSlab * 0.20); float secondSlab = 8000 - 4000; secondSlab = secondSlab - (float) (secondSlab * 0.15); float firstSlab = 8000 - 4000; total = firstSlab - (float) (firstSlab * 0.1); total = total + secondSlab + thirdSlab + fourthSlab; } return total; } } 

    Based on the comment by the OP, I created more adaptable code. I'm not sure what pattern this is, except I used a factory to build the rewards.

    I created a Tier class to hold a tier, a Reward class to hold a reward, and a RewardFactory to define the rewards. This should make it easier to change tiers or add new reward types.

    If a new reward concept is created, then some code would have to be added.

    Here's the revised code.

    import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; public class ShoppingCartDiscount { static NumberFormat formatter = NumberFormat.getCurrencyInstance(); public static void main(String args[]) { ShoppingCartDiscount scd = new ShoppingCartDiscount(); RewardFactory rewardFactory = scd.new RewardFactory(); String rewardType = "Regular"; float amount = 5_000f; float discount = rewardFactory.calculateDiscount( rewardType, amount); displayDiscount(rewardType, amount, discount); amount = 10_000f; discount = rewardFactory.calculateDiscount( rewardType, amount); displayDiscount(rewardType, amount, discount); amount = 15_000f; discount = rewardFactory.calculateDiscount( rewardType, amount); displayDiscount(rewardType, amount, discount); System.out.println(); rewardType = "Premium"; amount = 4_000f; discount = rewardFactory.calculateDiscount( rewardType, amount); displayDiscount(rewardType, amount, discount); amount = 8_000f; discount = rewardFactory.calculateDiscount( rewardType, amount); displayDiscount(rewardType, amount, discount); amount = 12_000f; discount = rewardFactory.calculateDiscount( rewardType, amount); displayDiscount(rewardType, amount, discount); amount = 20_000f; discount = rewardFactory.calculateDiscount( rewardType, amount); displayDiscount(rewardType, amount, discount); } static void displayDiscount(String rewardType, float amount, float discount) { System.out.print(rewardType); System.out.print(" customer spends "); System.out.print(formatter.format(amount)); System.out.print(", so we discount "); System.out.print(formatter.format(discount)); System.out.print(", so he owes "); amount -= discount; System.out.print(formatter.format(amount)); System.out.println("."); } public class RewardFactory { private List<Reward> rewards; public RewardFactory() { this.rewards = new ArrayList<>(); createRewards(); } private void createRewards() { Reward reward = new Reward("Regular"); Tier tier = new Tier(0f, 5_000f, 0.00f); reward.addTier(tier); tier = new Tier(5_000f, 10_000f, 0.10f); reward.addTier(tier); tier = new Tier(10_000f, Float.MAX_VALUE, 0.20f); reward.addTier(tier); rewards.add(reward); reward = new Reward("Premium"); tier = new Tier(0f, 4_000f, 0.10f); reward.addTier(tier); tier = new Tier(4_000f, 8_000f, 0.15f); reward.addTier(tier); tier = new Tier(8_000f, 12_000f, 0.20f); reward.addTier(tier); tier = new Tier(12_000f, Float.MAX_VALUE, 0.30f); reward.addTier(tier); rewards.add(reward); } public float calculateDiscount(String rewardType, float amount) { float discount = 0f; for (Reward reward : rewards) { if (reward.isDiscountApplied(rewardType)) { discount += reward.calculateDiscount(amount); } } return discount; } } public class Reward { private final String rewardType; private List<Tier> tiers; public Reward(String rewardType) { this.rewardType = rewardType; this.tiers = new ArrayList<>(); } public void addTier(Tier tier) { this.tiers.add(tier); } public boolean isDiscountApplied(String type) { return (rewardType.equalsIgnoreCase(type)); } public float calculateDiscount(float amount) { float discount = 0f; for (Tier tier : tiers) { if (tier.isDiscountApplied(amount)) { discount += tier.calculateDiscount(amount); } } return discount; } } public class Tier { private final float lowerAmount; private final float upperAmount; private final float percentDiscount; public Tier(float lowerAmount, float upperAmount, float percentDiscount) { this.lowerAmount = lowerAmount; this.upperAmount = upperAmount; this.percentDiscount = percentDiscount; } public boolean isDiscountApplied(float amount) { return (lowerAmount < amount); } public float calculateDiscount(float amount) { if (amount > upperAmount) { return (upperAmount - lowerAmount) * percentDiscount; } else { return (amount - lowerAmount) * percentDiscount; } } } } 
    \$\endgroup\$
    3
    • \$\begingroup\$@Uday Kiran: See my updated answer.\$\endgroup\$CommentedApr 18, 2020 at 17:05
    • \$\begingroup\$Amazing !! I liked the solution. could you give some tips where I can learn Object Oriented Design programming. Reference video links would be appreciated.\$\endgroup\$CommentedApr 18, 2020 at 18:10
    • \$\begingroup\$@Uday Kiran: I'm not ignoring your question. I don't know how to answer it. In my career, I hardly used any of the concepts in books or videos. I rarely used inheritance, and maybe created 4 to 5 interfaces. I've used other people's inheritance classes and interfaces. I had to code Java for 9 months or so before I really understood objects and what they could do.\$\endgroup\$CommentedApr 19, 2020 at 10:13

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.