It would be great to see actual architectures implementing IOC using container oriented approaches. I am sure it will change the way we think about interaction between components.
Please feel free to download my free 500 question and answer videos which covers Design Pattern, UML, Function Points, Enterprise Application Blocks, OOP'S, SDLC, .NET, ASP.NET, SQL Server, WCF, WPF, WWF, SharePoint, LINQ, SilverLight, .NET Best Practices @ these videos http://www.questpond.com/
So let us understand in detail about IOC and DI.
Design pattern – Inversion of control and Dependency injection
- The problem – tight coupling
- Principles of IOC
- Ways of implementing IOC
- Implementing the DI
- What is wrong with DI FACTORY?
- The container way
- Implementation using Windsor
ClsCustomer’ class also. 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.
Figure: - Problems of IOC
There are two principles of IOC:
- Main classes aggregating other classes should not depend on the direct implementation of the aggregated classes. 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.
Figure: - IOC frameworkFigure ‘IOC framework’ shows how we can achieve this decoupling. The simplest way would be to expose a method which allows us to set the object. Let the address object creation be delegated to the IOC framework. IOC framework can be a class, client 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.
Figure: - IOC and DIFigure ‘IOC and DI’ 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
- Interface implementation
- Service locator
In the further sections we will walkthrough the same in more detail.
Figure: - Constructor based DI
Figure: - Getter and Setter
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
Figure: - Service locator
Here are the issues with factory which makes us force to think about some other solutions:
- Everything is hardcoded: - The biggest issues with factory are it can not be reused across applications. All the options are hardcoded in the factory itself which makes the factory stringent to particular implementation.
- Interface dependent: - The base on which factories stands are common interfaces. Interfaces decouple the implementation and the object creation procedure. But then all the classes should implement a common interface. This is a limitation by itself again.
- Factories are custom: - They are very much custom to a particular implementation.
- Everything is compile time: - All dependent objects for an object in factory have to be known at compile time.
Figure: - Container in actionSo you can think about container as a mid man who will register address and customer objects as separate entity and later the container creates the customer and address object and injects the address object in the customer. So you can visualize the high level of abstraction provided by containers.
What we will do is cover the customer and address example using one of the container Windsor container, you can get more details about the container here.
Figure: - Address interfaceIn the customer class we have passed the object through the constructor.
Figure: - Customer classIf we are said to write the client code. , it would be something as shown in figure ‘Client code’. In step 1 we create a concrete object and point the implementation to the interface IAddress. In step 2 we pass the interface object to customer class constructor while creating the object.
Figure: - Client codeOk, 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 and concrete objects 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. Step 5 releases the customer object.
Figure: - Windsor containerOk, guys understood, the above code is more complicated than the client code. In actual implementation using the container we never use client code, rather we use config files. You can see from figure ‘Creating using config files’ we have better flexibility to add more objects. 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. In config file we need to define all the components in the components section.
Figure: - Creating using config files