The term IOC (Inversion of Control) and DI (Dependency Injection) have often been used interchangeably. Both concepts have been around in software design for a long time and just have became in spotlight recently due to, I guess, the concept of decoupling software components to increase maintainability , make cleaner code and encourage unit-testing has became more and more popular.
There are many frameworks to help developers to implement the concepts but the most adopted one is Spring framework. The fact that Spring call its dependency injection framework “IOC container” so, with its incredible popularity, people start using IOC as the synonym of DI. Technically, IOC is a software design principle which has a more generic meaning than DI. DI is just a specific form of IOC.
I am not going to pretend to be an expert in software design. If you want to hear from the real expert then I recommend you to read Martin Fowler’s article on the concept of IOC and how it related to DI and just forget about the rest of my blog. The core concept I am going to tell you I derived it from those two articles.
What is Inversion of Control?
IOC is about how software components or areas of code give up its “control” to other components or other areas of code. The “control” may be execution flow, object life-cycle managing, the way subcomponent has been created or any program activity.
Let’s look at an example
ServerSocket echoServer = new ServerSocket(8888);
while(true){
Socket clientSocket = echoServer.accept();
ClientHandlerThread handler = new ClientHandlerThread(clientSocket);
handler.start();
}
The above area of code use standard java API to create a server program. It start by binding socket to port 8888 , put it self in forever loop waiting for client connection. Once it got a connection, it decides to create a thread to handle the connection then get back to while-loop to wait for next request. You can think of the above code as a user program which use java network API as a library. The program controls its own execution flow and manages its own life-cycle (in this case the server stop when administrators manually kill its process). The library can be look as a passive component with the user program actively drives the whole program.
Now, let’s say we want a HTTP server. There are so many details to handle when you deal with HTTP protocol. It’s complicate enough for java as a platform to create a framework for you so you don’t need to get your hand dirty in dealing with low level details.
With Servlet API, you only have to concern about your business logic by specify what you want to do with clients requests in doGet/doPost methods. The API and web container will intercept socket connection can call the method you have implemented.
In this case, the control invert from your code to the framework. The framework will drive the whole program. It will call init() to let you have a chance to initialize your component before accepting request and will call destroy() when it decide your servlet shouldn’t be active anymore. This is way IOC is also known as the Hollywood Principle – “Don’t call us, we’ll call you”.
What is Dependency Injection?
DI is a specific form of IOC. In this case, the “control” that is inverted is the way your component manage its dependency.
class MyComponent{
private ConfigParser parser;
public MyComponent(String configFile){
parser = new ConfigParser(configFile);
}
public void init(){
String debugStr = parser.getConfig("debug");
}
}
MyComponent internally create ConfigParser in it constructor. This will make MyComponent tightly depend on ConfigParser. What if, in the future, you create another version of ConfigParser which read parameters from a configuration repository server? MyComponent has to come up with a way to choose which implementation to use. Not just MyComponent, all code that explicitly create ConfigParser have to be changed to accommodate a new type of parser.
Another thing is the above code is very hard to do unit testing. The ConfigParser implementation always read parameters form file. Imagine you are writing JUnit test case for MyComponent class with 20 test scenario and you have to create a configuration file for each scenario.
interface ConfigParser{
public String getConfig(String configKey);
}
class FileConfigParser implements ConfigParser{
public FileConfigParser(String configFile) { //init }
public String getConfig(String string) { //Read Parameter from file. }
}
class RemoteConfigParser implements ConfigParser{
public RemoteConfigParser(String host, int port) { //init }
public String getConfig(String string) { //Read Parameter from remote server. }
}
class MyComponent{
private ConfigParser parser;
public MyComponent(ConfigParser configParser){
this.parser = configParser;
}
public void init(){
String debugStr = parser.getConfig("debug");
}
}
Now, our MyComponent doesn’t explicitly create an implementation of ConfigParser. The control of how to create ConfigParser has been inverted to be a responsibility of someone else or put it another way, someone will create a concrete implementation of ConfigParser (dependency) and inject it to MyComponent. This dependency injection pattern help promote loosely couple system. MyComponent class doesn’t entirely depend on a specific type of ConfigParser, any parser implementation can be injected to MyComponent becase all implementations fulfill the same contracts specified in ConfigParser interface. In JUnit test case you can create a mock parser which read parameters from a StringReader containing just those parameters for a specific test scenario.
RemoteConfigParser is likely to have its own dependency, let’s say it’s a component encapsulate connection transport. You do the same as you did with ConfigParser- externalize the dependency into Transport interface to make someone else be able to inject concrete transport into RemoteConfigParser. Now what if a transport implementation has a few dependencies?
You can get the picture that now our system comprises of many components with its dependency hierarchy. Before you are able to use a component, you have to resolve its whole dependency hierarchy. In the large system which contains hundred or thousand components, who is going to do that for you.
Now it might not be very hard to guess what Spring IOC container is for. I will refactor our program to make RemoteConfigParser has two Transport dependency. Here is the Spring IOC configuration file which I will used to tell spring how to resolve MyComponent dependency hierarchy.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="primary-transport" class="example.ioc.TCPTransport">
<constructor-arg value="172.20.12.16" />
<constructor-arg value="8888" />
</bean>
<bean id="backup-transport" class="example.ioc.TCPTransport">
<constructor-arg value="172.20.12.17" />
<constructor-arg value="8888" />
</bean>
<bean id="configParser" class="example.ioc.RemoteConfigParser">
<constructor-arg ref="primary-transport" />
<constructor-arg ref="backup-transport" />
</bean>
<bean id="myComponent" class="example.ioc.MyComponent">
<constructor-arg ref="configParser" />
</bean>
</beans>
Somewhere it my program initialization I will ask Spring to create MyComponent for me.
String configFile = "mycomponent-config.xml";
ApplicationContext appContext = new ClassPathXmlApplicationContext(configFile);
MyComponent myComponent = (MyComponent)appContext.getBean("myComponent");
I don’t know it is correct or not to call a DI framework a “IOC container” but IOC and DI is not 100% equivalent as you may think.


ภาษาดีขั้นเทพครับ