Quote for the Week

"Learn to enjoy every moment of your life"

Tuesday, January 27, 2015

S.O.L.I.D Architecture principle in C# (Part 2)

Continue to Friday, January 23, 2015
-----------------------------------------------

O: Open/Closed Principle -

The Open/closed Principle says "A software module/class is open for extension and closed for modification".

Let's say you created a class that provides certain functionality. A few months later some need arises such that the class is supposed to provide some additional functionality. In such cases you shouldn't touch the source code of the class that is already built. Instead, you should be able to extend it so as to add the extra functionality.

Let's try to understand this with an example. Suppose that you are building a class that is responsible for calculating taxes on a given income. At the time of development you were told that this class will be used for tax calculation in three countries, say USA, UK and India. The following code shows a simplistic representation of such a class.


public class TaxCalculator
{
    public decimal CalculateTax(decimal amount,string country)
    {
        decimal taxAmount = 0;
        switch(country)
        {
            case "USA":
                //calculate tax as per USA rules
                break;
            case "UK":
                //calculate tax as per UK rules
                break;
            case "IN":
                //calculate tax as per India rules
                break;
        }
        return taxAmount;
    }
}

The TaxCalculator class has a method CalculateTax() that accepts the amount on which the tax is to be calculated and the country whose tax calculation rules are to be applied. Inside, a switch statement checks the country and accordingly the tax is calculated and returned to the caller.

Now, suppose that after a few months you also need to calculate tax in a few more countries. How will you take care of this additional functionality? You will change the CalculateTax() method and add additional cases inside the switch statement. That means any change in the functionality is forcing you to change the core class - TaxCalculator. This is violation of OCP.

Now, let's rewrite our code so that it follows OCP.

public abstract class TaxCalculatorBase
{
    public decimal TotalAmount { get; set; }
    public abstract decimal CalculateTax();
}
 
public class USATax:TaxCalculatorBase
{
    public override decimal CalculateTax()
    {
        //calculate tax as per USA rules
        return 0;
    }
}
 
public class UKTax : TaxCalculatorBase
{
    public override decimal CalculateTax()
    {
        //calculate tax as per UK rules
        return 0;
    }
}
 
public class IndiaTax : TaxCalculatorBase
{
    public override decimal CalculateTax()
    {
        //calculate tax as per India rules
        return 0;
    }
}

The above code creates an abstract class TaxCalculatorBase. This class defines a public property - TotalAmount - and an abstract method CalculateTax(). The CalculateTax() method doesn't have any code since it is an abstract method. The actual tax calculation happens in the derived classes - USATax, UKTax and IndiaTax. These classes provide the concrete implementation of the CalculateTax() method. Tomorrow if tax calculation is needed for a few more countries you need not modify any of the existing code. All you need to do is create another class that inherits from TaxCalculatorBase class and implement the required tax calculation logic inside its CalculateTax() method.

No comments: