3/16/2025

IoC vs DI vs DIP

 1.Inversion of Control (IoC)

A fundamental principle that inverts traditional control flow
Enables decoupling of components from their dependencies
Provides flexibility in how objects are created and managed

2.Dependency Inversion Principle (DIP)

A design guideline that specifies how modules should relate
Ensures high-level modules don't depend directly on low-level ones
Promotes abstraction-based relationships

3.Dependency Injection (DI)

A concrete technique for implementing IoC
Provides a specific mechanism for delivering dependencies
Helps achieve the goals outlined by DIP

IoC is the broad principle that enables various patterns, DIP guides how modules relate to each other, and DI provides a specific technique for implementing both concepts. Together, they form a powerful foundation for building maintainable software systems.

3/15/2025

Liskov Substitution Principle



Example 1:
Without LSP:
Example, we have different Services, Service1 for soap version1 and Service2 for soap version2, now new feature is introduced in soap version2 called ReportFaultData() but for soap version1 it is not there, so we need to change Service1 by adding ReportFaultData() in interface and service1 should do " throw NotSupportedException("Reporting fault data is not supported");" , this leads to compile time error or we need to do nothing, and so we need to change other classes.


But if Service2 silently does nothing:

  • The caller thinks the reboot happened ✅
  • But it actually didn't ❌
  • This creates hidden bugs, inconsistent behavior, and silent failures ❌
  • The program becomes unpredictable ❌
  • Debugging becomes extremely hard ❌


With LPS:
Instead we will create new interface ISupportsFaultDataReporting and add the new ReportFaultData() and inherit only for Service1 class.

public interface IService
{
    string GetControllerName();
    void Reboot();
}

public interface IReportFaultData
{
    void ReportFaultData();
}


public class Service1 : IService
{
    public string GetControllerName()
    {
        return "Service2 Controller";
    }

    public void Reboot()
    {
        // Service2 reboot logic
    }

    // No ReportFaultData() — because Service1 cannot report faults 
}


public class Service2 : IService, IReportFaultData
{
    public string GetControllerName()
    {
        return "Service1 Controller";
    }

    public void Reboot()
    {
        // actual reboot logic
    }

    public void ReportFaultData()
    {
        // reporting logic
    }
}


public class Program
{
    public static void Main()
    {
        IService service;

        if (_soapVersion <= 1)
            service = new Service1();
        else
            service = new Service2();

        Console.WriteLine(service.GetControllerName());
        service.Reboot();

        if (service is IReportFaultData reporter)
        {
            reporter.ReportFaultData();
        }
    }
}
Example 2:

Without LSP:
Example, we are creating different robots, Kawasaki, Yaskawa, and ABBRobots all inheriting IRobots and have methods like Connect(), Operate() , Move(). Later say some new functionality in ABBRobots like ReportFaultData() is introduced, so we need to added ReportFaultData() in interface and other robots should do " throw NotSupportedException("Reporting fault data is not supported");" , this leads to compile time error or we need to do nothing, and so we need to change other classes.


With LPS:
Instead we will create new interface ISupportsFaultDataReporting and add the new ReportFaultData() and inherit only for ABBRobots 

public class ABBRobots : IRobots, ISupportsFaultDataReporting 
{
}

public class Program
{
    public static void Main()
    {
        List robots = new List{new Kawasaki(),new Yaskawa(),new ABBRobots()};
        foreach (var robot in robots)
        {
            robot.Move();
            if (robot is ISupportsFaultDataReporting faultDataReportingRobot)
            {
                faultDataReportingRobot.Report();
            }
        }
    }
}
Without LSP:
Why the name substitution - The idea is that you should be able to substitute the base class with the derived class without breaking the functionality.

 // Substituting base class with dereived class
        Shape shape = new Rectangle(5, 10);