Showing posts with label SOLID Design Principles. Show all posts
Showing posts with label SOLID Design Principles. Show all posts

Dependency Inversion Principle Introduction

In this Article, we will discuss 

·                     Dependency Inversion Principle Introduction
·                     Understand the intention of DIP usage

In the first video of SOLID Introduction we have understood that D in SOLID stands for Dependency Inversion Principle which is also known as DIP

The Dependency Inversion Principle introduced by Robert C Martin states that   

·                     High-level modules should not depend on low-level modules. Both should depend on abstractions.

AND

·                     Abstractions should not depend on details. Details should depend on abstractions.

To simplify this we can state that while designing the interaction between a high-level module and a low-level one, the interaction should be thought of as an abstract interaction between them.  

Usage Intention : Before understanding the intention of usage, let’s try to understand a traditional application architecture implementation. 

During the process of the application design, lower-level components are designed to be consumed by higher-level components which enable increasingly complex systems to be built. In this Process of Composition, higher-level components depend directly upon lower-level components to achieve some task.  

This dependency upon lower-level components limits the reuse opportunities of the higher-level components and ends up in a bad design. 

This dependency upon lower-level components limits the reuse opportunities of the higher-level components and ends up in a bad design. 

dependency inversion principle 

From the illustrated diagram, High-level Modules depends directly on Low-level Modules and this does not follow the first point of DIP. 

Allowing the dependency causes many issues and we have discussed many of them in our first session of this tutorial. For now, we are not going to get into details of those as it’s out of scope of this session. However, we strongly recommend you to refer to the first part of this tutorial before proceeding. 

dependency inversion principle c# 

Now let’s take a look at button click event example from the Presentation layer that calls directly the Employee Save Method of Business logic which does some validation checks before saving the employee details to DB. 

This flow looks absolutely fine however we are coupling different layers and as said earlier, any further changes are complicated and cumbersome.

Now, Let’s see this problem with a sample code. 

public class BusinessLogicLayer
{
    private readonly DataAccessLayer DAL;

    public BusinessLogicLayer()
    {
        DAL = new DataAccessLayer();
    }

    public void Save(Object details)
    {
        DAL.Save(details);
    }
}

public class DataAccessLayer
{
    public void Save(Object details)
    {
        //perform save
    }
}


Notice from the above code the BLL is directly dependent on the low level Data Access Layer and it’s hard to perform any unit tests on this code as both are coupled. Of course, we can do some amount of testing on the DAL but imagine if the DAL needs to be further extended to SQL and XML layers. If we need to that, implementation to extend it, becomes tedious and much more complicated. 

Hence based on the DIP we apply an Abstraction to decouple these layers.

In order to achieve that we introduce interface that acts as abstraction so that both are decoupled.

This decoupling is demonstrated in the representation diagram and further we can re-write our code as shown on the screen. 

dependency inversion principle tutorial

If you take a look at this code based on the pictorial representation diagram we have introduced the Interface which is extended by the Data Access Layer and referred in the BLL. Hence we can say that Irepository layer acts as abstraction between these modules. 

public class BusinessLogicLayer
{
    private readonly IRepositoryLayer DAL;

    public BusinessLogicLayer(IRepositoryLayer repositoryLayer)
    {
        DAL = repositoryLayer;
    }

    public void Save(Object details)
    {
        DAL.Save(details);
    }
}


public interface IRepositoryLayer
{
    void Save(Object details);
}


public class DataAccessLayer : IRepositoryLayer
{
    public void Save(Object details)
    {
        //perform save
    }
}



Adapter Design pattern can be seen as an example which is applying the dependency inversion principle. 

dependency inversion principle example 

The high-level class defines its own adapter interface which is the abstraction that the other high-level classes depend on. The Adaptee implementation also depends on the adapter interface abstraction.


Liskov Substitution Principle

Liskov Substitution Principle
In this Article, we will learn 

·                     Liskov Substitution Principle
·                     Implementation guidelines of Liskov Substitution Principle
·                     And will implement this Principle with a simple example

 L in the SOLID is an acronym for Liskov Substitution Principle which is also known as LSP

Definition : Substitutability is a principle in object-oriented programming and it states that, in a computer program, if S is a Subtype of T, then objects of type T may be replaced with objects of type S 

·                     Which means, Derived types must be completely substitutable for their base types
·                     More formally, the Liskov substitution principle (LSP) is a particular definition of a subtyping relation, called (strong) behavioral subtyping
·                     This Principle is introduced by Barbara Liskov in 1987 during her conference address on Data abstraction and hierarchy
·                     This principle is just an extension of the Open Close Principle
The examples used in this session are related to the open closed principle. Hence we request you to watch the Open Closed Principle tutorial before proceeding. 

Implementation guidelines: In the process of development we should ensure that  

·                     No new exceptions can be thrown by the subtype unless they are part of the existing exception hierarchy.
·                     We should also ensure that Clients should not know which specific subtype they are calling, nor should they need to know that. The client should behave the same regardless of the subtype instance that it is given.
·                     And last but not the least, New derived classes just extend without replacing the functionality of old classes
In the previous session as part of the Open closed Principle implementation, we have created different employee classes to calculate bonus of the employee. From the employee perspective, we have implemented the Open closed principle. 

Now if you take a look at the main program, we have created Employee objects which consists of both permanent and contract employee

If you take a closer look at this program the Derived types which are Permanent and TemporaryEmployee have completely substituted the base type employee class.

So, based on the Liskov substitution principle we have achieved LSP by ensuring that Derived types are completely substitutable for their base types.

Also, notice the main program, without using the subtypes we are calculating the bonus of the employee from the base class type itself. Hence, we are satisfying the Liskov substitution principle. 

That means along with the Open Closed Principle we have partially implemented the LSP. 

Also, I can state that this implementation is not adhering to guidelines of Liskov principle

To understand why it’s not adhering to the Liskov Principle, Let’s assume that we need to have a Contract Employee as one of the employee categories. A point to note here is a contract employee is not eligible for any bonus calculation and post implementing the Employee class we end up throwing an exception at the runtime in the caclculatebonus() method. This violates the Liskov Substitution Principle. 

Hence, Please follow the below code which addresses this issue. Also, we recommend watching our video tutorials for complete guidance and understanding of the code.

Code before Liskov Substitution Principle. 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenClosedDemo
{
    public abstract class Employee
    {
        public int ID { getset; }
        public string Name { getset; }
       
        public Employee()
        {
        }


        public Employee(int id, string name )
        {
            this.ID = id; this.Name = name; 
        }

        public abstract decimal CalculateBonus(decimal salary);
       
        public override string ToString()
        {
            return string.Format("ID : {0} Name : {1}"this.ID, this.Name);
        }
    }


    public class PermanentEmployee : Employee
    {
        public PermanentEmployee()
        { }

        public PermanentEmployee(int id, string name) : base(id, name)
        { }
        public override decimal CalculateBonus(decimal salary)
        {
            return salary * .1M;
        }
    }

    public class TemporaryEmployee : Employee
    {
        public TemporaryEmployee()
        { }

        public TemporaryEmployee(int id, string name) : base(id, name)
        { }
        public override decimal CalculateBonus(decimal salary)
        {
            return salary * .05M;
        }
    }

    public class ContractEmployee : Employee
    {
        public ContractEmployee()
        { }

        public ContractEmployee(int id, string name) : base(id, name)
        { }
        public override decimal CalculateBonus(decimal salary)
        {
            throw new NotImplementedException();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenClosedDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Employee empJohn = new PermanentEmployee(1, "John"  );
            Employee empJason = new TemporaryEmployee(2, "Jason" );
            Employee empMike = new ContractEmployee(3, "Mike" );
            Console.WriteLine(string.Format("Employee {0} Bonus: {1}",
                empJohn.ToString(),
                empJohn.CalculateBonus(100000).ToString()));
            Console.WriteLine(string.Format("Employee {0} Bonus: {1}",
              empJason.ToString(),
              empJason.CalculateBonus(150000).ToString()));
            Console.WriteLine(string.Format("Employee {0} Bonus: {1}",
              empMike.ToString(),
              empMike.CalculateBonus(150000).ToString()));

            Console.ReadLine();
        }
    }
}


Above code throws an error at empMike, as Bonus is not applicable to ContractEmployee. In that case LSP is violated and we have redefined the code to follow LSP below.

Code after Implementing Liskov Substitution Principle.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSPDemoConsole
{
    interface IEmployee
    {
        int ID { getset; }
        string Name { getset; }
        decimal GetMinimumSalary();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSPDemoConsole
{
    interface IEmployeeBonus
    {
        decimal CalculateBonus(decimal salary);
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LSPDemoConsole
{
    public abstract class Employee : IEmployee, IEmployeeBonus
    {
        public int ID { getset; }

        public string Name { getset; }


        public Employee()
        { }


        public Employee(int id, string name)
        {
            this.ID = id;
            this.Name = name;

        }


        public abstract decimal CalculateBonus(decimal salary);

        public override string ToString()
        {
            return string.Format("ID : {0} Name : {1}"this.ID, this.Name);
        }

        public abstract decimal GetMinimumSalary();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSPDemoConsole.Implementation
{
    public class PermanentEmployee : Employee
    {
        public PermanentEmployee()
        { }

        public PermanentEmployee(int id, string name) : base(id, name)
        { }


        public override decimal CalculateBonus(decimal salary)
        {
            return (salary * .1M);
        }

        public override decimal GetMinimumSalary()
        {
            return 15000;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSPDemoConsole.Implementation
{
    public class TemporaryEmployee : Employee
    {
        public TemporaryEmployee()
        { }

        public TemporaryEmployee(int id, string name) : base(id, name)
        { }


        public override decimal CalculateBonus(decimal salary)
        {
            return (salary * .05M);
        }


        public override decimal GetMinimumSalary()
        {
            return 5000;
        }
    }

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSPDemoConsole.Implementation
{
    public class ContractEmployee : IEmployee
    {
        public int ID { getset; }

        public string Name { getset; }
        public ContractEmployee()
        { }

        public ContractEmployee(int id, string name)
        {
            this.ID = id;
            this.Name = name;
        }

        public decimal GetMinimumSalary()
        {
            return 5000;
        }

        public override string ToString()
        {
            return string.Format("ID : {0} Name : {1}"this.ID, this.Name);
        }
    }
}

using LSPDemoConsole.Implementation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSPDemoConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Employee> employees = new List<Employee>();
            employees.Add(new PermanentEmployee(1, "John"));
            employees.Add(new TemporaryEmployee(2, "Jason"));
            //Un Comment to see the error
            //employees.Add(new ContractEmployee(3, "Mike"));
            foreach (var employee in employees)
            {
                Console.WriteLine(string.Format("Employee {0} Bonus: {1} MinSalary: {2}",
                employee.ToString(),
                employee.CalculateBonus(100000).ToString(),
                employee.GetMinimumSalary().ToString()));
            }

            Console.WriteLine();

            List <IEmployee> employeesOnly = new List<IEmployee>();

            employeesOnly.Add(new PermanentEmployee(1, "John"));
            employeesOnly.Add(new TemporaryEmployee(2, "Jason"));
            employeesOnly.Add(new ContractEmployee(3, "Mike"));

            foreach (var employee in employeesOnly)
            {
                Console.WriteLine(string.Format("Employee {0}  MinSalary: {1}",
                employee.ToString(),
                employee.GetMinimumSalary().ToString()));
            }
            Console.ReadLine();
        }
    }
}


I believe this video has given you a good idea on how we can implement Liskov substitution principle