Java
Article
By Alejandro Gervasio

Three Design Patterns That Use Inversion of Control

By Alejandro Gervasio

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.

  • Efraim Globus

    great article. interesting analyzation of the IoC pattern

    • Alejandro Gervasio

      Hi Efraim,

      Thanks for the positive feedback. Glad to know you enjoyed the article.

  • Pavi.8081

    It appears that UserFifoQueue.get() and UserLifoQueue.get() implementations needs to be swapped.

    • Alejandro Gervasio

      Hi Pavi,

      Thanks for the comment indeed.UserFifoQueue.get() and UserLifoQueue.get() implementations are correct. It’s quite tricky to follow their logic at first, but they’re right.

      Cheers!

      • Pavi.8081

        Hmm, I see! you are right. That’s because your add() is defined as
        public void add(User user) {
        queue.addFirst(user);
        }

        which is always adding at the head of queue.

        But don’t you think this is opposite to what is generally expected about the implementation of such methods?
        I mean generally its expected that new elements are added to the tail of the queue and removal happens from the head of it.
        So given your way of implementation does it not impact the code readability?
        I understand its very simple code but don’t you think in a larger scale this could severely impact code readability and maintainability?
        After all these are also important parameters to consider when structuring and implementing code.

        Oh! and yes, let me also mention that your article is indeed good and has given me newer perspective to see discussed patterns. Thanks! for that

        Cheers!

Recommended
Sponsors
Get the latest in Java, once a week, for free.