Replace Conditional with Polymorphism

Replace Conditional with Polymorphism:

  • This technique adheres to the Tell-Don't-Ask principle: instead of asking an object about its state and then performing actions based on this, it is much easier to simply tell the object what it needs to do and let it decide for itself how to do that.
  • Removes duplicate code. You get rid of many almost identical conditionals.
  • If you need to add a new execution variant, all you need to do is add a new subclass without touching the existing code (Open/Closed Principle).
Lets see the EMI calculator example with traditional switch case :

package com.dp.refactor;

public class EMICalculatorTraditional {
    
    public double getEMI(String type,double amount,int periodInMonths){
        switch(type){
        case "HomeLoan":
            return (amount*9*Math.pow(1+9,periodInMonths))/(Math.pow(1+9,periodInMonths)-1);
        case "CarLoan":
            return (amount*10*Math.pow(1+10,periodInMonths))/(Math.pow(1+10,periodInMonths)-1);
        }
        return 0;
        
    }
    public static void main(String[] args) {
        EMICalculatorTraditional emiCalculatorTraditional = new EMICalculatorTraditional();
        System.out.println(emiCalculatorTraditional.getEMI("HomeLoan",50000, 12));
    }


Now what if i need to add one more new EMICalculator that is PersonalLoanEMI calculator.So what i need to do is check over all the code and look where do i need to make changes and again need to test whole application to check if anything else is not affected by my code change.And also this breaks open Close principle of Object Oriented Programming,as we are modifying existing classes.So instead of this create base interface for common action and add concrete classes for all brances of the switch case.And in future if you want to add new branch just add new concrete class implementing base interface.So by doing that you do not need to test whole application,you just need to test your added functionality as you have not touched the existing functionality.This is somewhat similar to strategy design pattern.
See the below modified code:

Base Interface:
package com.dp.refactor;

public interface EMICalculator {
    
    public double calculateLoanInterest(double amount,int periodInMonths);

}


Concrete Classes:
1.HomeLoanEMICalculator:
package com.dp.refactor;

public class HomeLoanEMICalculator implements EMICalculator {

    private static final double rate = 9;
    @Override
    public double calculateLoanInterest(double amount, int periodInMonths) {
        return (amount*rate*Math.pow(1+rate,periodInMonths))/(Math.pow(1+rate,periodInMonths)-1);
    }

}


2.CarLoanEMIcalculator:
package com.dp.refactor;

public class CarLoanEMICalculator implements EMICalculator {

    private static final double rate = 10;
    @Override
    public double calculateLoanInterest(double amount, int periodInMonths) {
        return (amount*rate*Math.pow(1+rate,periodInMonths))/(Math.pow(1+rate,periodInMonths)-1);
    }

}


Client :
package com.dp.refactor;

public class Client {
    
    public static void main(String[] args) {
        
        EMICalculator emiCalculator = new HomeLoanEMICalculator();
        System.out.println(emiCalculator.calculateLoanInterest(50000, 12));
    }

}

Now if you want to add new EMICalculator type as PersonalLoanEMIcalculator you just need to add one more concrete PersonalLoanEMICalculator class implementingEMICalculator base interface.
Thats all
Thanks

Comments