Three Design Patterns That Use Inversion of Control

Share this article

Three Design Patterns That Use Inversion of Control

For many developers, inversion of control (IoC) is a fuzzy concept, with little or no application in the real world. In the best of cases, it’s considered just a plain equivalent of dependency injection (DI). The IoC = DI equation is only true, though, when both sides reference inverting the control of dependency management. While dependency injection is actually a well-known form of IoC, the truth is that IoC is a much broader software design paradigm, which can be implemented through several patterns. In this article we’ll be taking a look at how dependency injection, the observer pattern, and the template method pattern implement inversion of control.

Like many other design patterns from the rich repertoire available out there, implementing IoC is a trade-off for the developer:

  • The design of highly-decoupled components and the encapsulation of application logic in one single place are direct and natural consequences of implementing IoC.
  • The flip side is that the implementation requires building at least one layer of indirection and in some use cases this might just be overkill.

Looking at a few concrete implementations will help you make the trade-off between these properties.

Demystifying the IoC Paradigm

Inversion of control is a pattern with several slants. A typical example of IoC is given by Martin Fowler in the following simple program that collects user data from the console:

public static void main(String[] args) {
    while (true) {
        BufferedReader userInputReader = new BufferedReader(
                new InputStreamReader(System.in));
        System.out.println("Please enter some text: ");
        try {
            System.out.println(userInputReader.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

In this case, the program’s flow of control is defined by the main method: In an infinite loop, it reads user input and it prints it to the console. Here, the method fully controls when to read user input and when to print it.

Now, consider a revamped version of the program, which uses a graphical user interface (GUI) for collecting input data through a text field box, a button and an action listener bound to it. In this context, every time the user clicks the button the entered text is collected by the listener and printed to a panel.

In this version of the program, it’s actually under the control of the event listener model (in this case, that’s the framework) to call the code written by the developer for reading and printing user input. Simply put, the framework will call the developer’s code, rather than the other way around. The framework is in fact an extendable structure that provides the developer with a set of specific points for injecting segments of custom code.

In this sense, the control has been effectively inverted.

From a more generic point of view, each callable extension point defined by a framework, either in the form of interface implementation(s), implementation inheritance (aka subclassing) is a well-defined form of IoC.

Consider the case of a simple Servlet:

public class MyServlet extends HttpServlet {

    protected void doPost(
            HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // developer implementation here
    }

    protected void doGet(
            HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // developer implementation here
    }

}

Here, the HttpServlet class (belonging to the framework) is the element that has the full control of the program, not the MyServlet subclass. The code in the doGet() and doPost() methods are automatically called in response to GET and POST HTTP requests by the servlet after being created by the servlet container.

Compared to a typical inheritance perspective, where subclasses have the control instead of the base class, the control has been inverted.

In fact, the servlet’s methods are an implementation of the template method pattern, which we’ll discuss in depth later on.

In the case of frameworks that stick to the open/closed principle by providing an extendable API, the role of the developer using the framework boils down to defining their own set of custom classes, either by implementing one or multiple interfaces provided by the framework or by inheriting from existing base classes. In turn, instances of the classes are directly instantiated from and called by the framework.

To quote Fowler:

The framework calls the developer, rather than the developer calls the framework.

Hence that IoC is often called the Hollywood Principle:

Don’t call us, we’ll call you.

Inversion of Control used by Dependency Injection, Observers, and the Template Method Pattern

Implementing Inversion of Control

At this point, it becomes evident that there are several methodologies for implementing inversion of control. Let’s go through a roundup on how to implement the most common ones.

IoC through Dependency Injection

As it was stated before, DI is just one form of IoC, and quite possibly one of the most ubiquitous ones used in object-oriented design. But let’s think through this: In which way does DI actually invert the control?

To answer this, let’s create a naive example:

public interface UserQueue {

    void add(User user);

    void remove(User user);

    User get();

}

public abstract class AbstractUserQueue implements UserQueue {

    protected LinkedList<User> queue = new LinkedList<>();

    @Override
    public void add(User user) {
        queue.addFirst(user);
    }

    @Override
    public void remove(User user) {
        queue.remove(user);
    }

    @Override
    public abstract User get();

}

public class UserFifoQueue extends AbstractUserQueue {

    public User get() {
        return queue.getLast();
    }

}

public class UserLifoQueue extends AbstractUserQueue {

    public User get() {
        return queue.getFirst();
    }

}

The UserQueue interface defines the public API of a simple queue that stores user objects (the implementation of the User class is omitted here for brevity’s sake), whereas AbstractUserQueue provides some shared implementation further down the hierarchy. Lastly, UserFifoQueue and UserLifoQueue, implement basic FIFO and LIFO queues.

This is an effective manner of implementing subtype polymorphism. But what does this buy us in concrete terms? Pretty much, actually.

By creating a client class that declares a dependency to a UserQueue abstract type (aka a service in DI terminology), different implementations can be injected at run time, without refactoring the code that uses the client class:

public class UserProcessor {

    private UserQueue userQueue;

    public UserProcessor(UserQueue userQueue) {
        this.userQueue = userQueue;
    }

    public void process() {
        // process queued users here
    }

}

UserProcessor shows in a nutshell why DI is actually a form of IoC.

We could have placed the control on how to acquire the dependency on the queue in UserProcessor by directly instantiating them in the constructor, through some hard-coded new operators. But this is the typical code smell that introduces a strong coupling between client classes and their dependencies and sends testability to its doom. I hear some alarm bells ringing in my ears! Don’t you? Yes, that would be bad design.

Instead, the class declares a dependency upon the abstract type UserQueue in the constructor, thus it is no longer under its control to look up its collaborator through a new operator in the constructor, Conversely, the dependency is injected from the outside, either by using a DI framework (CDI and Google Guice are neat examples of outsider injectors), or just plain old-school factories and builders.

In a nutshell, with DI the control on how dependencies are acquired by client classes no longer resides in these classes; it resides in the injectors instead:

public static void main(String[] args) {
     UserFifoQueue fifoQueue = new UserFifoQueue();
     fifoQueue.add(new User("user1"));
     fifoQueue.add(new User("user2"));
     fifoQueue.add(new User("user3"));
     UserProcessor userProcessor = new UserProcessor(fifoQueue);
     userProcessor.process();
}

This will work as expected and injecting the UserLifoQueue implementation is pretty straightforward, too. It’s clear to see that DI is a just a way of achieving inversion of control (in this case DI is a layer of indirection for implementing IoC).

IoC through the Observer Pattern

Another straightforward way of implementing IoC is via the observer pattern. In a broader sense, the way in which observers invert the control is similar to the one of an action listener in the context of a GUI. While in the case of action listeners, they get called in response to a specific user event (a mouse click, several keyboard / window events and so forth), observers are usually used to keep track of the changes in the state of a model object in a model-view context.

In a classic implementation, one or multiple observers are bound to the observable object (aka the subject in the pattern’s terminology), for example by calling an addObserver method. Once the bindings between the subject and the observer(s) have been defined, the observers are called in response to a change in the state of the subject.

To better understand this concept, consider the following example:

@FunctionalInterface
public interface SubjectObserver {

    void update();

}

This very simple observer is called whenever a value changes. In real life it should provide a richer API, for example by containing the changed instance or the old and new value, but those are not required to watch the pattern in action, so I kept it simple.

Here comes an observable class:

public class User {

    private String name;
    private List<SubjectObserver> observers = new ArrayList<>();

    public User(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
        notifyObservers();
    }

    public String getName() {
        return name;
    }

    public void addObserver(SubjectObserver observer) {
        observers.add(observer);
    }

    public void deleteObserver(SubjectObserver observer) {
        observers.remove(observer);
    }

    private void notifyObservers(){
        observers.stream().forEach(observer -> observer.update());
    }

}

The User class is just a naive domain class, which notifies the attached observer(s) whenever its state is changed through the setter methods.

With the SubjectObserver interface and the subject in place, here’s how an instance can be observed:

public static void main(String[] args) {
    User user = new User("John");
    user.addObserver(() -> System.out.println(
            "Observable subject " + user + " has changed its state."));
    user.setName("Jack");
}

Each time the state of the user object is modified through the setter, the observer gets notified and consequently it prints out a message to the console. So far this has been a fairly trivial application of the observer pattern. What’s not so trivial, though, is to see how the control is inverted in this case.

With the observer pattern, the subject becomes “the framework” that wields the control over who gets called and when. It is the observers whose control is taken away because they have no influence over when they get called (as long as they remain registered with the subject). This means we can actually spot the one line where control is inverted – it is when the observer is bound to the subject:

user.addObserver(() -> System.out.println(
            "Observable subject " + user + " has changed its state."));

This shows in a nutshell why the observer pattern (or an action listener in a GUI-driven environment) is a fairly simple manner of achieving IoC. It’s in this form of decentralized design of software components where the inversion of control takes place.

IoC through the Template Method Pattern

The motivation behind the template method pattern is to define a generic algorithm in a base class through several abstract methods (aka the algorithm steps), and let subclasses provide specific implementations of them, while keeping the algorithm structure unchanged.

We could apply this concept and define a generic algorithm for processing domain entities:

public abstract class EntityProcessor {

    public final void processEntity() {
        getEntityData();
        createEntity();
        validateEntity();
        persistEntity();
    }

    protected abstract void getEntityData();
    protected abstract void createEntity();
    protected abstract void validateEntity();
    protected abstract void persistEntity();

}

The processEntity() method is the template method that defines the structure of the algorithm that processes entities, and the abstract methods are the algorithm steps, which must be implemented by the subclasses. Several versions of the algorithm could be created by subclassing EntityProcessor as many times as required, and providing different implementations of the abstract methods.

While this shows the motivations pushing behind the template method pattern, one might wonder why the pattern is a form of IoC.

In a typical implementation of inheritance, the subclasses call the methods defined in the base class. In this case, the opposite actually happens: the methods implemented by the subclasses (the algorithm steps) are called in the base class through the template method. So, it’s in the base class where the control actually resides, not in the subclasses.

This is another classic example of IoC, achieved through a hierarchical structure. In this case, template method is just a fancy name for defining a callable extension point, which is used by the developer to provide their own set of implementations.

Summary

Even though inversion of control is prevalent in the Java ecosystem, particularly in the many frameworks available out there and the ubiquitous adoption of dependency injection, for many developers the pattern still remains vague and largely limited to getting dependencies injected. In this article, I clarified this concept by showcasing several approaches that can be followed for implementing IoC in the real world.

  • Dependency Injection: The control on how dependencies are acquired by client classes no longer resides in these classes. It resides in the underlying injectors / DI framework(s) instead.

  • Observer pattern: The control is transferred from the observers to the subject when it comes to reacting to changes.

  • Template method pattern: The control resides in the base class that defines the template method, instead of in the subclasses, implementing the algorithm’s steps.

As usual, how and when to use IoC is something that should be evaluated on a per-use case basis, without falling into a pointless blind worshiping.

Frequently Asked Questions (FAQs) about Inversion of Control

What is the main difference between Inversion of Control (IoC) and Dependency Injection (DI)?

Inversion of Control (IoC) and Dependency Injection (DI) are two design patterns that are often used interchangeably, but they are not the same. IoC is a broader concept that refers to a programming style where a framework or runtime controls the program flow. On the other hand, DI is a form of IoC where the control of creating and injecting dependencies is inverted. In DI, instead of a class creating its dependencies, they are provided to it, often through a constructor or a setter method.

How does Inversion of Control improve code quality?

IoC improves code quality by promoting loose coupling and enhancing modularity. By inverting the control, the dependencies between classes are reduced, making the code more modular and easier to manage, test, and reuse. It also improves code readability and maintainability, as each component can be understood, developed, and tested in isolation.

Can you provide an example of Inversion of Control in Java?

Yes, a common example of IoC in Java is the Spring Framework. In Spring, the IoC container is responsible for creating and managing objects, known as beans. The container uses configuration metadata provided by the developer to create these beans and manage their lifecycle, effectively inverting the control from the developer to the framework.

What are the different types of Inversion of Control?

There are three main types of IoC: Dependency Lookup, Dependency Injection, and Service Locator pattern. Dependency Lookup is where a component is responsible for finding its dependencies. Dependency Injection is where dependencies are provided to a component. Service Locator pattern is a form of IoC where a service locator object is responsible for retrieving the dependencies.

What are the benefits of using Inversion of Control?

IoC offers several benefits. It promotes loose coupling and enhances modularity, making the code easier to manage, test, and reuse. It also improves code readability and maintainability. Furthermore, it allows for better separation of concerns, as each component can be developed and tested in isolation.

What are the drawbacks of using Inversion of Control?

While IoC offers many benefits, it also has some drawbacks. It can make code more complex and harder to understand, especially for developers unfamiliar with the concept. It can also lead to increased startup time, as the IoC container needs to initialize all the beans at startup.

How does Inversion of Control relate to the Single Responsibility Principle?

IoC supports the Single Responsibility Principle by ensuring that each class or module has only one reason to change. By inverting the control, each component is only responsible for its core functionality, and the responsibility of managing dependencies is delegated to the IoC container.

How does Inversion of Control work in the Spring Framework?

In the Spring Framework, the IoC container is responsible for creating and managing beans. The container uses configuration metadata provided by the developer to create these beans and manage their lifecycle. This effectively inverts the control from the developer to the framework.

Can Inversion of Control be used in languages other than Java?

Yes, IoC is a design pattern that can be used in any object-oriented programming language, not just Java. It is commonly used in languages like C#, Python, and Ruby.

Is Inversion of Control the same as Dependency Inversion Principle?

No, IoC and the Dependency Inversion Principle (DIP) are not the same, although they are related. DIP is a principle that states that high-level modules should not depend on low-level modules, but both should depend on abstractions. IoC is a design pattern that can be used to achieve this principle by inverting the control of creating and managing dependencies.

Alejandro GervasioAlejandro Gervasio
View Author

Alejandro Gervasio is a senior System Analyst from Argentina who has been involved in software development since the mid-80's. He has more than 12 years of experience in PHP development, 10 years in Java Programming, Object-Oriented Design, and most of the client-side technologies available out there.

dependency injectiondesign patternsinversion of controlnicolaipObserver PatternTemplate Method Pattern
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week