Labels

Thursday, October 7, 2010

Design pattern - IOC & DI

The Inversion of Control (IoC) pattern, is also known as Dependency Injection. Or In other words - IOC is implemented using DI. Or DI is the main method to implement IoC.
The target behind applying IOC and DI is to build Loosely Coupled Software Architecture.

IoC is not a new concept, however. It has been around for several years now. Using OO design principles and features such as Interface, Inheritance, and Polymorphism, the IoC pattern enables better software design that facilitates reuse, Loose Coupling, and easy testing of software components

The basic principle of IOC stands on the base of Bollywood principle – "Do not call us we will call you".
In other words it like aggregated class (here Address) saying to the container class (here Customer), do not create me I will create myself using some one else.

This article discusses IoC and demonstrates how to use this pattern in your software design with/without using any of the open source frameworks.

IoC Design Pattern

Lets understand this with an example.

Assume Class A has a relationship with Class B: it wants to use the services of Class B. The usual way to establish this relationship is to instantiate Class B inside Class A.




public class A
{
  private B b;

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

Listing 1 - Class A Directly Refers Class B 


public class A
{
  private B b;

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

Listing 2. Class A Directly Refers Class B and Class C

Though this approach works, it creates tight coupling between the classes. You can't easily change Class B without modifying Class A.
Here are the problems.

·         This design assumes that - Class B is a concrete class that has a default constructor. Now If Class B is changed to have a non-default constructor, then Class A would also require a change.
·         Also If Class B changed to have a non-default constructor, which takes Class C , then again the Class A would require a change.
·         Now If Object "a" owns both Object "b" and Object "c". If Class B or Class C changes at all, then Class A needs to change as well.

In essence, a simple design of a simple class with implicit design assumptions becomes a maintenance nightmare in the future. Consider how difficult making changes would be if you had this scenario in a typical application with several classes.

Here comes the usage of 'IoC pattern Framework'.

Let's re-understand the above problem with realistic objects.
In the above code, the biggest issue is the tight coupling between classes. The same is found here in real-time example also.

IOC - Problem



Here, the Customer class depends on the Address object. So for any reason Address class changes it will lead to change and compiling of 'ClsCustomer' class also. I.e. You can't easily change Address class without modifying 'ClsCustomer' Class. For e.g.

·         If Class Address is changed to have a non-default constructor, then Class Customer would also require a change.
·         Now If Object "Customer" owns two Objects "Address" and "SocietyDetail". If these two Class changes at all, then Class Customer needs to change as well.

So let's put down problems with this approach:

  • The biggest problem is that Customer class controls the creation of Address object.
  • Address class is directly referenced in the Customer class which leads to tight coupling between address and customer objects.
  • Customer class is aware of the Address class type. So if we add new Address types like home address, office address it will lead to changes in the customer class also as customer class is exposed to the actual address implementation.

In essence - If for any reason the Address object is not able to create,  the whole Customer class will fail in the constructor initialization itselfVery High/Tight Coupling.

Solution: IoC

The main problem roots from the customer class creating the address object. The solution is:

·         Shift the task/control of object creation from the customer class to some other entity – Problem Solved.
·         In other sentence if we are able to Invert this control to a third party we have found our solution. So the solution name is IOC (Inversion of control).

This is the opposite of using an API, where the developer's code makes the invocations to the API code. Hence, frameworks Invert the control: it is not the developer code that is in charge, instead the framework makes the calls based on some stimulus. I.e. Do not call us we will call you.

This solutiuon Achieves The Decoupling, and is refrerred to as - IOC (Inversion of control).

Here is the solution detail - Expose a method which allows us to set the address object. I.e. Let the address object creation be delegated to the IOC framework. IOC framework can be a class, client class or some kind of IOC container. So it will be two step procedure IOC framework creates the address object and passes this reference to the customer class.

Solution – Using DI

clsAddress objAddress = new clsAddress(..);

clsCustomer objCustomer = new clsCustomer;
objCustomr.setAddress(objAddress); // IOC Framework


The basic principle of IOC stands on the base of Hollywood principle - Do not call us we will call you (Translating for struggling actors)
In other words it like Address class saying to the Customer class, Do Not Create Me I Will Create Myself Using Some One Else.

There are two principles of IOC:

  • Main classes (Here like Customer) aggregating other classes (Here like Address) should not depend on the direct implementation of the aggregated classes (Here like Address). Both the classes should depend on abstraction. So the customer class should not depend directly on the address class. Both address and customer class should depend on an abstraction either using interface or abstract class.
  • Abstraction should not depend on details, details should depend on abstraction.


Ok, now we know the problem, let's try to understand the broader level solution. IOC is implemented using DI (Dependency injection).
We have discussed on a broader level about how to inject the dependency in the previous sections. In this section we will dive deeper in to other ways of implementing DI.

Several open source IoC frameworks like Windsor Container, Castle Windsor, Spring, PicoContainer, and HiveMind support the IoC pattern. While the general IoC principle is simple, each of these frameworks supports different implementations and offers different benefits. The IoC pattern can be implemented in four ways, as mentioned below.




The figure shows how IOC and DI are organized. So we can say IOC Is A Principle while DI Is A Way Of Implementing IOC. In DI we have four broader ways of implementing the same:

  • Constructor way
  • Exposing setter and getter (As in above examples)
  • Interface implementation
  • Service locator

Constructor Methodology

In this methodology we pass the object reference in the constructor itself. So when the client creates the object he passes the object in the constructor while the object is created.
Its main advantage is that only the creator knows about the referenced object.
This methodology is not suited for client who can only use default constructors. Then you need to go with a setter-based IoC.

IOC - Constructor Methodology

Figure: - Constructor based DI

Setter and Getter

This is the most commonly used DI methodology. The dependent objects are exposed through set/get methods of classes.
Setter-based IoC is good for objects that take optional parameters and objects that need to change their properties many times during their lifecycles.
The bad point is because the objects are publicly exposed it breaks the encapsulation rule of object oriented programming.

IOC – Setter & Getter

Figure: - Getter and Setter

 

In this type of IoC, objects implement a specific interface from the IoC framework, which the IoC framework will use to properly inject the objects.
One of the main advantages of this type is that it doesn't need an external configuration file to configure the object references. Since you need to use the IoC framework's marker interface, the IoC framework knows how to glue the objects together.
The main disadvantage of this approach is that the marker interface ties your application to a specific IoC framework. 

You can see in figure 'Interface based DI' we have implemented an interface 'IAddressDI' which has a 'setAddress' method which sets the address object.
This interface is then implemented in the customer class. External client / containers can then use the 'setAddress' method to inject the address object in the customer object.



Figure: - Interface based DI



The other way to inject dependency is by using service locator.
Your main class in need to aggregate the child object, will use the service locator to obtain instance of the address object.
The service locator class does not create instances of the address object, it provides a methodology to register and find the services which will help in creating objects.



Figure: - Service locator




Here we'll see how we can implement DI using 'Programatically' and 'Open Source IoC Framework - Windsor container'

If you are starting a new project, you can choose any of the open source IoC frameworks based on your needs.
If you want to use the IoC pattern in your existing project then you need to write your own classes that support IoC. Though the open source frameworks offer off-the-shelf components and may provide many more features than your own implementation, you can still develop a set of classes that support the IoC pattern using either of the above approach.

Here for example – Constructor Approach.

interface IAddress
{
   void AddAddress(string address);
}
public class Address : IAddress
{
   public string address;

   public void AddAddress(string address)
   {
      this.address = address;
   }
}

public class ClsCustomer
{
   IAddress address;

   public ClsCustomer(IAddress address)
   {
     this.address = address;
   }

   public string GetAddress()
   {
      Type c = Type.GetType("Address", true, true);
      if (typeof(IAddress).IsAssignableFrom(c))
      {
         return (objAddress as Address).MyAddress;
      }
      return string.Empty;
   }
}

class Client
{
   public void Run()
   {
      IAddress objAddress = new Address();
      objAddress.AddAddress("India");
           
      ClsCustomer customer = new ClsCustomer(objAddress);
      string address = customer.GetAddress();
   }       
}



Ok, now lets see how this will work if we use the Windsor container.

Figure 'Windsor container' shows how it looks like. So

·         Step 1 creates the Windsor container object.
·         Step 2 and 3 register the types (Iaddress) and concrete objects(clsAddress, clsCustomer) in the container.
·         Step 4 requests the container to create the customer object. In this step the container resolves and set the address object in the Constructor.
It's like - ClsCustomer customer = new ClsCustomer(objAddress);
·         Step 5 releases the customer object.
 
IOC – Interface Based


Figure: - Windsor container 

In actual implementation using the container we never use client code as above, rather we use config files.
You can see from figure 'Creating using config files' we have better flexibility to add more objects. In config file we need to define all the components in the components section.

The XmlInterpreter object helps to read the config file to register the objects in the container.
Using the Container.Resolve method we have finally created the customer object.
So the container plays the mediator role of understanding the Customer object and then injecting the Address object in the customer object through the constructor.

 



Figure: - Creating using config files


References: Link1, Link2

Hope this helps.

Thanks & Regards,
Arun Manglick

No comments:

Post a Comment