⭐ If you would like to buy me a coffee, well thank you very much that is mega kind! : https://www.buymeacoffee.com/honeyvig
Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies
Showing posts with label SOLID. Show all posts
Showing posts with label SOLID. Show all posts

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.

Single Reponsibility Principle in .NET

Recently, I was reading about SOLID principles and how they can help us write better code. SOLID principles help make our code more supple so it can easily change with change in requirements. It helps us in writing our code more towards object oriented design rather than procedural code. It also helps make our code less rigid as well as less fragile and easy to reuse. I will be discussing all these principles one by one continuing in subsequent posts.

In today's blog post, I will be discussing one of the SOLID principles which can help in better design and implementation of the system, i.e. Single Responsibility Principle (SRP).


Single Responsibility Principle (SRP)

According to this principle, a class should have only one reason to change. If the class is doing too much then there might be multiple reasons for it's changes.
In other words, each class should do only one thing and do it well. It helps in separations of concerns. Let's look at an example to understand this:

For example you have a method like this:

public class Employee

{

      /*

        other properties and methods 

      */

      public ActionResult Details(int? id)

        {

            Log.Debug("Reading Employee " + employeeId);

            if (id == null)

            {

             return new 
             HttpStatusCodeResult(HttpStatusCode.BadRequest);

            }

            Employee employee = db.Employees.Find(id);

            if (employee == null)

            {

                Log.Debug("Employee not Found" + employeeId);

                return HttpNotFound();

            }

            Log.Debug("Returning Employee " + employeeId);

            return View(employee);

        }

      //....

}

By looking at this code, you can clearly see that this method is doing more than intended. For retrieving the employee, it's doing all the logging as well. If in future, we need to change logging mechanism, then we will have to revisit the code, as well as if we change the way employees are retrieved, then also we need to change this code. So there are multiple reasons for this class to change.

So, one way to solve this problem is to have a separate logger like this:


public class EmployeeLogger

    {

        public void Reading(int id)

        {

            Log.Debug("Retrieving Employee " + id);

        }

        public void Returning(int id)

        {

            Log.Debug("Returning Employee " + id);

        }

        public void NotFound(int id)

        {

            Log.Debug("No Employee found " + id);

        }

    }


Now, the Employee class might look like:


public class Employee

{

      private EmployeeLogger _logger = new EmployeeLogger(); 

      /*

        other properties and methods 

      */

public ActionResult Details(int? id)

        {

            _logger.Reading(employeeId);

            if (id == null)

            {

             return new 
             HttpStatusCodeResult(HttpStatusCode.BadRequest);

            }

            Employee employee = db.Employees.Find(id);

            if (employee == null)

            {

                _logger.NotFound(employeeId);

                return HttpNotFound();

            }

            _logger.Returning(employeeId);

            return View(employee);

        }

//... 

}

So now the logging responsibility belongs to EmployeeLogger class. If in future, we need to change the way employee logging is supposed to be done, we can change only the EmployeeLogger and don't need to make changes to the Employee Details method.
Currently, you might think that we have written a lot more code for a very small benefit. But, as the code base grows larger and the requirements change, you will see the benefit of it in the long run. Also, this provides us as a good base for better design and other SOLID principles to apply upon, as we will see in subsequent posts.

Conclusion

The SRP is one of the easiest to understand out of the SOLID principles but it's hard to get it right. Following the principle at each and every time might be an overkill but in general we should try to adhere to it as and when needed. The understanding of when to apply the principle and to what extent comes with experience and practice.

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

Thursday, August 20, 2015

Liskov Substitution Principle in .NET

In today's blog post, I will be discussing Liskov Substitution principle i.e. "L" in SOLID.

Liskov Substitution Principle

According to LSP, the derived classes should be substitutable for their base classes without altering the correctness of the program. In other words, when implementing inheritance in your application, instead of "Is A" relationship, think in terms of "Is Substitutable For" relationship.
Let's look at the famous sqaure-rectangle example to better understand this:

Geometrically, a square is a rectangle. So this may lead us to derive square class from a rectangle class.

public class Rectangle
{
        public virtual int Height { get; set; }
        public virtual int Width { get; set; }

        public int GetArea()
        {
            return Height*Width;
        }
}


public class Square : Rectangle
{
        public override int Height
        {
            get { return base.Height; }
            set { base.Height = base.Width = value; }
        }

        public override int Width
        {
            get { return base.Width; }
            set { base.Height = base.Width = value; }
}


Imagine, we have a test method like this:

 public void TestRectangles()
{
            var r1 = new Rectangle()

            {

                Height = 2,

                Width = 3

            };

            var r2 = new Rectangle()

            {

                Width = 3,

                Height = 2

            };

            var isHeightEqual = r1.Height == r2.Height;

            var isWidthEqual = r1.Width == r2.Width;
}

 So isHeightEqual & isWidthEqual will be true.

However, if we substitute this test example with Square instead of rectangle, we will have:

public void TestRectangles()

{

            var r1 = new Square()

            {

                Height = 2,

                Width = 3

            };

            var r2 = new Square()

            {

                Width = 3,

                Height = 2

            };

            var isHeightEqual = r1.Height == r2.Height;

            var isWidthEqual = r1.Width == r2.Width;

}


So now isHeightEqual & isWidthEqual will be false. As a client, the correctness of my program has been compromised by substituting base class by derived class. So it is a violation of LSP.

One way that you might wanna structure this is by having a Quadrilateral base class and derive Square and Rectangle class from the Quadrilateral class.

public class Rectangle : Quadrilateral
{

        public int Height { get; set; }

        public int Width { get; set; }

        public int GetArea()

        {

            return Height*Width;

        }
}



public class Square : Quadrilateral
{

        public int Size { get; set; }

        public int Area()

        {

            return Size*Size;

        }
}


So a square and rectangle are substitutable for quadrilateral.

Another way might be to not use square class at all. Just use the rectangle class and set height and width equal manually and use it in your code.

Conclusion

This is a simple example to demonstrate the LSP. If our derived class is not perfectly substitutable for base type, then it means that we should start looking at the restructuring of our classes, if possible. Otherwise, it can lead to side effects and may lead to incorrect behavior in the system. Inheritance is a powerful tool in developer's arsenal but it should be used carefully. We should give thought to external behaviors of the entities we are using in inheritance and be able to substitute one for another.

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