Friday, August 21, 2015

Open Closed Principle in .NET

In today's blog post, I will be discussing the second principle out of the 5 SOLID principles i.e. Open-Closed principle(OCP). 


Open Closed Principle

According to this principle, a class should be open for extension but closed for modification.
We all know that the only thing that is constant in this world, and more so in software, is change. The requirements change all the time and many times we keep adding new functionality on top of the existing ones. This principle guides us that instead of modifying existing classes, we can extend the existing classes to add new functionality. This makes our code easy to reuse and maintain.

Let's understand this with an example:

Suppose we have a class like this:

public class Product

{

        public decimal Price { get; set; }

        public DiscountType DiscountType { get; set; }

}

where DiscountType is an enum with 2 enumerators:

public enum DiscountType

{

    None = 0,

    Sale = 1

}

And suppose, you have a PriceCalculator class like this:

public class PriceCalculator

{

    public decimal GetTotalPrice(Product[] products)

    {

        decimal sum = 0;

        foreach (var product in products)

        {

            if (product.DiscountType == DiscountType.None)

                sum += product.Price;

            if (product.DiscountType == DiscountType.Sale)

                sum += product.Price*(decimal) 0.9;

        }

        return sum;

    }

}


Now the code works fine and return you the Total Price of the product. For all regular price items, you return the full price. For sale items you provide 10% discount.
Now, the client comes in and says I want to add another type of discount here, say super sale with 20% discount. We can cater to client requirements by adding another enumerator to the DiscountType enum and add another if condition in the PriceCalculator. Now, next time the client comes in and asks for mega Sale and so on. Looking at the price calculator method, you can imagine that this can get pretty ugly and difficult to maintain. So instead of adding to the enum and if conditions in GetTotalPrice(), we can close our PriceCalculator class for modification and extend the existing classes like this:

First, we will need to abstract out our Product class like this:

    public interface IProduct

    {

        decimal Price { get; set; }



        decimal TotalPrice();

    }

Then, we can add specific classes for Regular Product and Sale Product:

public class RegularProduct : IProduct

{

    public decimal Price { get; set; }

    public decimal TotalPrice()

    {

        return Price;

    }

}




 public class SaleProduct : IProduct

 {

      public decimal Price { get; set; }

      public decimal TotalPrice()

      {

          return Price*(decimal) 0.9;

      }

 }

Now, the PriceCalculator class will look like this:

public class PriceCalculator

{

        public decimal GetTotalPrice(IProduct[] products)

        {

         return products.Sum(product => product.TotalPrice());

        }

}

So, we don't need to change this GetTotalPrice() any more. We can keep adding any new product types as the requirements come up by implementing IProduct.

So for adding superSale item, we can simply add SuperSaleProduct which implements IProduct and our GetTotalPrice() will remain unchanged. And similarly, if the amount of discount needs to be changed for existing SaleProduct then also we don't need to change the existing GetTotalPrice().

Conclusion

So we saw how Open-Closed principle can help us write code that is easy to extend and maintain. 
The closure in changes for any class is mostly theoretical. This is a simplified example to illustrate what the principle is and how we can adhere to it. In real world, it might not be so simple or practical to use it all the time. But when we have such a scenario and we can simplify our code using this principle, we should follow it. 

For future updates to my weekly blog, please subscribe to my blog via the "Subscribe To Weekly Post" feature at the right.

No comments:

Post a Comment