What is inversion of control?
Inversion of Control
can be quite confusing to understand and hard to explain. Inversion of Control(IoC) helps to write pluggable code
that needs less refactoring. What do I mean by that? Let’s dive through.
To keep this post within the context of this site, I’ll limit the examples only to Java
. But the concept of IoC
by itself is not limited to a single programming language.
Applications without IoC
Let’s take an example. In an object-oriented programming environment, An object A may need another object B’s functionality to run. So it is obvious that somewhere in the Object A’s logic, Object B is created. This is also known as tight-coupling
. Let’s see this below example,
package com.springhow.examples.beans;
public interface Fruit {
String juice();
}
import com.springhow.examples.beans.Fruit;
public class Apple implements Fruit {
@Override
public String juice() {
return "Here is Apple Juice";
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Juicer {
private static final Logger log = LoggerFactory.getLogger(Juicer.class);
public void extractJuice(Fruit fruit) {
String juice = fruit.juice();
logger.info(juice);
}
}
import com.springhow.examples.beans.Fruit;
import com.springhow.examples.beans.Juicer;
import com.springhow.examples.beans.impl.Apple;
public class NotAnIoCApplication {
public static void main(String[] args) {
Juicer juicer = new Juicer();
Fruit fruit = new Apple();
juicer.extractJuice(fruit);
}
}
This will print
19:17:57.178 [main] INFO com.springhow.examples.beans.Juicer - Here is Apple Juice
In this example, an object of Apple
is created by us and was given to Juicer
object. You may wonder what is wrong with this approach. But let’s just take a few steps back.
If You want to use another Fruit
object, Say Orange
. Then you need to change the program. At least the line Fruit fruit = new Apple();
. This means you are making it difficult for anyone else to use your program.
This what I call as bad code. Because you are the one who is plugging in all the components. The application code control HOW, WHEN and WHAT Fruit
object is created.
To make this program work with different Fruit without refactoring, you need a concept called Inversion Of Control.
Control means something else
Imagine country A get’s its oil supply from Country B. Here we can say that Country B has control over A because A is dependent on B’s supplies. If somehow A can easily switch between different countries for its supply, A can avoid B’s control entirely.
This is what control
in Inversion of Control
really means. It is about removing dependencies. We will see this with an example.
An Attempt to de-couple
The word inversion is a confusing term because it is relative to how you see it. Inversion of control is a concept of freedom. It helps to decouple components and make them less dependent on each other.
Check this below example.
fruit.class.name=com.springhow.examples.beans.impl.Orange
import com.springhow.examples.beans.Fruit;
import com.springhow.examples.beans.Juicer;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class SimpleIoCApplication {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
InputStream inputStream
= SimpleIoCApplication.class.getResourceAsStream("/config.properties");
properties.load(inputStream);
Juicer juicer = new Juicer();
Class<?> fruitClass
= Class.forName(properties.getProperty("fruit.class.name"));
Fruit fruit = (Fruit) fruitClass.newInstance();
juicer.extractJuice(fruit);
}
}
We introduced a new config.properties
and added a little Java’s Reflection
magic, And now we have an inversion of control. This will print output related to Orange
as we configured in the properties.
19:07:49.116 [main] INFO com.springhow.examples.beans.Juicer - Here is Orange Juice
Once this program is compiled and shipped, you don’t need to make any refactoring to the program for every other Fruit
implementation.
Just change the properties
file and add a new Fruit
implementation to the classpath and you are good to go. This is one form of inversion of control.
You may argue that the application created object using
fruitClass.newInstance()
. But the application never knew which type ofFruit
is created.
The right way to Inversion of Control
The above implementation is called IoC by configuration files. And it is, not a scalable solution. There are good design patterns that help in achieving IoC. Some of them are listed below.
- Dependency Injection
- Service locator pattern
- Pico Containers
- Template method pattern
- Adapter pattern
Try to read about these if you are interested. We will focus more on Dependency injection in upcoming chapters.