Inversion of Control

In this chapter, we will learn about IoC and how to implement it. This would be the first step towards achieving loose coupled design as illustrated by the following figure.

Inversion of Control (IoC) is a design principle (although, some people refer to it as a pattern). As the name suggests, it is used to invert different kinds of controls in object oriented design to achieve loose coupling. Here, the control means any additional responsibilities a class has other than its main responsibility, such as control over the flow of an application, control over the flow of an object creation or dependent object creation and binding.

IoC is all about inverting the control. To explain in layman's term, suppose you drive a car to your work place, it means you control the car. IoC principle suggests to invert the control, meaning instead of driving the car yourself, you hire a cab where another person will drive the car. Thus it is called inversion of the control from you to the cab driver. You don't have to drive a car yourself and let the driver do the driving so that you can focus on your main work.

IoC principle helps in designing loosely coupled classes which make them testable, maintainable and extensible.

Let's understand how IoC inverts the different kinds of control.

Control Over the Flow of a Program

In a typical console application in C#, execution starts from the Main() function. The Main() function controls the flow of a program or in other words sequence of user interaction. Consider the following simple console program.

Example: Program Flow
namespace FlowControlDemo
{
    class Program
    {
        static void Main(string[] args)
        {
           bool continueExecution = true;
            do
            {
                Console.Write("Enter First Name:");
                var firstName = Console.ReadLine();

                Console.Write("Enter Last Name:");
                var lastName = Console.ReadLine();

                Console.Write("Do you want to save it? Y/N: ");

                var wantToSave = Console.ReadLine();

                if (wantToSave.ToUpper() == "Y")
                    SaveToDB(firstName, lastName);

                Console.Write("Do you want to exit? Y/N: ");

                var wantToExit = Console.ReadLine();

                if (wantToExit.ToUpper() == "Y")
                    continueExecution = false;

            }while (continueExecution);
         
        }

        private static void SaveToDB(string firstName, string lastName)
        {
            //save firstName and lastName to the database here..
        }
    }
}

In the above example, the Main() function of the program class controls the flow of a program. It takes user's input for the first Name and last name. It saves the data, continues or exits the console depending upon the user's input. So here, flow of the control through the Main() function.

IoC can be applied to the above program by creating a GUI based application such as the following windows based application wherein the framework will handle the flow of a program using events.

win form

This is a simple example of implementing IoC on the flow of a program.

Control Over the Dependent Object Creation

IoC can also be applied in the way we create objects of dependent class. First of all, let's understand what we mean by dependency here.

Consider the following example.

public class A
{
    B b;

    public A()
    {
        b = new B();
    }

    public void Task1() {
        // do something here..
        b.SomeMethod();
        // do something here..
    }

}

public class B {

    public void SomeMethod() { 
        //doing something..
    }
}

In the above example, class A calls b.SomeMethod() to complete its task1. Class A cannot complete its task without class B and so you can say "Class A is dependent on class B" or "class B is a dependency of class A".

In object oriented design approach, classes need to interact with each other in order to complete one or more functionalities of an application such as in the above classes A and B. Class A creates and manages the life time of an object of class B. Essentially it controls the creation and life time of objects of dependency class.

IoC principle suggests to invert the control, means separate the controlling stuff to another class. In other words, invert the dependency creation control from the class A to another class as shown below.

public class A
{
    B b;

    public A()
    {
        b = Factory.GetObjectOfB ();
    }

    public void Task1() {
        // do something here..
        b.SomeMethod();
        // do something here..
    }
}

public class Factory
{
    public static B GetObjectOfB() 
    {
        return new B();
    }
}

As you can see above, class A uses Factory class to get an object of class B. Thus, we have inverted the dependent object creation from class A to Factory. The class A no longer creates an object of class B instead it uses Factory class to get the object of class B.

Let's understand this using a more practical example.

In an object oriented design, classes should be designed in loosely coupled way. Loosely coupled means changes in one class should not force other classes to change, so the whole application can become maintainable and extensible. Let's understand this by using typical n-tier architecture as depicted by the following figure.

In the typical n-tier architecture, the User Interface (UI) uses Service layer to retrieve or save the data. The service layer uses the BusinessLogic class to apply business rules on the data. The BusinessLogic class depends on the DataAccess class which retrieves or saves the data to the underlying database. This is simple n-tier architecture design. Let's focus on the BusinessLogic and DataAccess class to understand IoC.

The following is an example of BusinessLogic and DataAccess classes for customer.

public class CustomerBusinessLogic
{
    DataAccess _dataAccess;

    public CustomerBusinessLogic()
    {
        _dataAccess = new DataAccess();
    }

    public string GetCustomerName(int id)
    {
        return _dataAccess.GetCustomerName(id);
    }
}

public class DataAccess
{
    public DataAccess()
    {
    }

    public string GetCustomerName(int id) {
        return "Dummy Customer Name"; // get it from DB in real app
    }
}

As you can see in the above example, the CustomerBusinessLogic class depends on DataAccess class. It creates an object of the DataAccess class to get customer data.

Now, let's understand what's wrong with the above classes.

In the above example, CustomerBusinessLogic and DataAccess are tightly coupled classes because CustomerBusinessLogic class includes the reference of concrete DataAccess class. It also creates an object of DataAccess class and manages the lifetime of an object.

Problems in the above example classes:

  1. CustomerBusinessLogic and DataAccess classes are tightly coupled classes. So, changes in the DataAccess class will lead to changes in the CustomerBusinessLogic class. For example, if we add, remove or rename any method in DataAccess class then we need to change CustomerBusinessLogic class accordingly.
  2. Suppose, customer data comes from different databases or web service in future we may need to create different classes for so it leads to changes in CustomerBusinessLogic class.
  3. CustomerBusinessLogic class creates an object of DataAccess class using new keyword. There may be multiple classes which use DataAccess class and create its object. So if you change the name of the class, then you need to find all the places in your source code where you create objects of DataAccess and make the change throughout the code. This is repetitive code for creating an object of same class and maintaining its dependencies.
  4. Because CustomerBusinessLogic class creates an object of concrete DataAccess class, it cannot be tested independently (TDD). DataAccess class cannot be replaced with mock class.

So, to solve the above problems and get a loosely coupled design, we can use IoC and DIP principles together. Remember, IoC is a principle not a pattern. It just gives high level design guidelines but does not give implementation details. You are free to implement IoC principle the way you want.

The following pattern (but not limited) implements IoC principle.

Pattern for IOC

Let's use Factory pattern to implement IoC in the above example as the first step towards attaining loosely coupled classes.

First, create a simple Factory class which returns an object of DataAccess class as shown below.

Example: DataAccess Factory
public class DataAccessFactory
{
    public static DataAccess GetDataAccessObj() 
    {
        return new DataAccess();
    }
}

Now, use this DataAccessFactory class in CustomerBusinessLogic class to get an object of DataAccess class.

Example: Use Factory Class to Retrieve Object
public class CustomerBusinessLogic
{

    public CustomerBusinessLogic()
    {
    }

    public string GetCustomerName(int id)
    {
        DataAccess _dataAccess =  DataAccessFactory.GetDataAccessObj();

        return _dataAccess.GetCustomerName(id);
    }
}

As you can see, CustomerBusinessLogic class uses DataAccessFactory.GetCustomerDataAccessObj() method to get an object of DataAccess class instead of creating it using new keyword. Thus, we have inverted the control of creating an object of dependent class from CustomerBusinessLogic class to DataAccessFactory class.

This is a simple implementation of IoC and the first step towards achieving fully loose coupled design. As mentioned in the previous chapter, we will not achieve complete loosely coupled classes by only using IoC. Along with IoC we also need to use DIP, Strategy pattern, and DI (Dependency Injection).

Let's move to the second step to understand DIP and how it helps in achieving loose coupled design in the next chapter.