Inversion of Control – The Hollywood Principle

There’s a consensus among programmers (myself included, so here’s my own public mea culpa) that Inversion of Control (IoC) is nothing more than a synonym for plain old Dependency Injection (DI). There’s a pretty intuitive reason that sustains this mindset: if the motivation behind DI is to promote the design of classes whose external collaborators are supplied by its surrounding context rather than inversely looking up them, the process can effectively been seen as a form of IoC.

But while the DI = IoC equation can be considered generally valid, the concept of inversion of control itself is actually much broader. In fact, it could be said that DI is a specific use case which exploits the benefits of IoC, but it’s far from being the only one. This leads us back to the start; if DI is just a pattern that relies on the strengths of IoC, what’s IoC really then?

Traditionally, application components have been designed to operate on and control the execution environment, an approach that delivers well to some extent. For instance, a logging module could be implemented to log data to a file, and how and when to log the data would be a entirely under control of the module. The log file (a part of the environment in this case) would be just an external, passive element with no influence on the way the module works. But let’s say we need to extend the module’s functionality and give it the ability for additionally logging data to a database, or eventually even via email. Upgrading the module to expose the extra functionality will make it grow in complexity, becoming more bloated as the logic required to attend to these additional duties is packaged behind the same API. The approach works, but won’t scale at all.

This tangled situation can be sorted out in a fairly simple manner. Instead of making the module completely responsible for logging data to multiple endpoints, we can transfer the responsibility straight to the external environment. The module’s implementation would remain ridiculously simple, limited to acting as a simple event dispatcher. On the flip side, the environment would be responsible for implementing all of the logic required to log data entirely independent from the module in question.

Not surprisingly, the process of inverting these responsibilities between components and the environment is formally known as Inversion of Control (or in a more relaxed jargon, The Hollywood Principle), and its implementation can be a real boost when it comes to developing extensible, highly-decoupled program modules.

Of course, IoC is a language-agnostic paradigm, and as such it’s possible to consume it in the PHP world without much fuss.

Achieving Inversion of Control – Observing Domain Objects

IoC has indeed an ubiquitous presence, so it’s pretty easy to find implementations of it production. The first use case that comes to mind is Dependency Injection, but there are many other cases equally demonstrative, especially when stepping on the terrain of Event-Driven Design. If you’re wondering in what parallel universe IoC gets along with event handling mechanisms, consider a classic in the GoF repertoire: the Observer pattern.

Used nearly everywhere, even client-side via JavaScript, observers are a shining example of the IoC concept in action; there’s a highly-decoupled subject focused on doing just a few narrow tasks without polluting the surrounding context while one or more external observers are responsible for implementing the logic required for handling the events triggered by the subject. How to handle the events, and even processing new ones, is entirely a responsibility of the observers rather than the subject.

An example might be a nice way to make my previous babbling a little bit clearer. So, let’s say we’ve implemented a primitive Domain Model which defines a one-to-many relationship between blog posts and comments. In this case we’ll be deliberately ambitious and give the model the ability for firing off an email to notify the system administrator when a new comment is added to a post.

Honestly, implementing such a feature without appealing to IoC would literally be a tangled mess, as we’d be asking the domain objects to doing something that’s way outside their scope. Instead, we could take an IoC-based approach and define the domain classes as follows:

<?php
namespace Model;

interface PostInterface
{
    public function setTitle($title);
    public function getTitle();
    
    public function setContent($content);
    public function getContent();
    
    public function setComment(CommentInterface $comment);
    public function getComments();
}
<?php
namespace Model;

class Post implements PostInterface, SplSubject
{
    private $title;
    private $content;
    private $comments  = [];
    private $observers = [];

    public function __construct($title, $content) {
        $this->setTitle($title);
        $this->setContent($content);
    }
    
    public function setTitle($title) {
        if (!is_string($title) 
            || strlen($title) < 2
            || strlen($title) > 100) {
            throw new InvalidArgumentException(
                "The post title is invalid.");
        }
        $this->title = $title;
        return $this;
    }

    public function getTitle() {
        return $this->title;
    }
    
    public function setContent($content) {
        if (!is_string($content) || strlen($content) < 10) {
            throw new InvalidArgumentException(
                "The post content is invalid.");
        }
        $this->content = $content;
        return $this;
    }
    
    public function getContent() {
        return $this->content;
    }

    public function setComment(CommentInterface $comment) {
        $this->comments[] = $comment;
        $this->notify();
    }
    
    public function getComments() {
        return $this->comments;
    }

    public function attach(SplObserver $observer) { 
        $id = spl_object_hash($observer);
        if (!isset($this->observers[$id])) {
            $this->observers[$id] = $observer;
        }
        return $this;
    }
    
    public function detach(SplObserver $observer) {
        $id = spl_object_hash($observer);
        if (!isset($this->observers[$id])) {
            throw new RuntimeException(
                "Unable to detach the requested observer.");
        }
        unset($this->observers[$id]); 
        return $this;
    }
    
    public function notify() {
        foreach ($this->observers as $observer) {    
            $observer->update($this);
        }
    }
}
<?php
namespace Model;

interface CommentInterface
{
    public function setContent($content);
    public function getContent();
    
    public function setAuthor($author);
    public function getAuthor();
}
<?php
namespace Model;

class Comment implements CommentInterface
{
    private $content;
    private $author;
    
    public function __construct($content, $author) {
       $this->setContent($content);
       $this->setAuthor($author);
    }
    
    public function setContent($content) {
        if (!is_string($content) || strlen($content) < 10) {
            throw new InvalidArgumentException(
                "The comment is invalid.");
        }
        $this->content = $content;
        return $this;
    }
    
    public function getContent() {
        return $this->content;
    }
    
    public function setAuthor($author) {
        if (!is_string($author) 
            || strlen($author) < 2
            || strlen($author) > 50) {
            throw new InvalidArgumentException(
                "The author is invalid.");
        }
        $this->author = $author;
        return $this;
    }
    
    public function getAuthor() {
        return $this->author;
    }
}

The interaction between the Post and Comment classes is trivial, but the Post class deserves an in-depth look. Effectively, it has been designed as a “classic” subject, hence providing the typical API which permits to attach/detach and notify observers at will.

The most interesting facet of this process is the implementation of setComment() where the actual inversion of control takes place. The method just fires a “pull” update to all the registered observers whenever a comment is added. This means that all the logic required for sending out the email notification is delegated to one or more external observers, thus offloading the dirty work from Post, keeping it focused on just its own business logic.

With this simple but effective schema of inversion of control in place, the only structure that needs to be added to the picture is at least one observer which should be responsible for dispatching the aforementioned email. To keep things easy to follow, I’m going to implement the observer as a thin entity living and breathing in the service layer.

Delegating Control to the External Environment – Implementing a Comment Notification Service

Building an observer service capable of triggering an email notification when a new comment is added to a blog post is a simple process, reduced to defining a class that implements the pertaining update() method. If you’re curious and want to see how the service in question looks, here it is:

<?php
namespace Service;

class CommentService implements SplObserver
{
    public function update(SplSubject $post) {
        $subject = "New comment posted!";
        $message = "A comment has been made on a post entitled " .
            $post->getTitle();
        $headers = "From: "Notification System" <notify@example.com>rnMIME-Version: 1.0rn";
        if (!@mail("admin@example.com", $subject, $message, $headers)) {
            throw new RuntimeException("Unable to send the update.");
        }
    }
}

The CommentService class does exactly what it’s supposed to; it invokes its update() method to dispatch an email to the sysadmin each time a user drops a comment related to a given post.

It’d be a lot easier to see the benefits brought by inversion of control in this situation if I showed you a script that puts all the sample classes to work, so below is some code:

<?php
use LibraryLoaderAutoloader,
    ModelPost,
    ModelComment,
    ServiceCommentService;
         
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader();
$autoloader->register();

$post = new Post(
    "A sample post",
    "This is the content of the sample post"
);

$post->attach(new CommentService());

$comment = new Comment(
    "A sample comment",
    "Just commenting on the previous post"
);

$post->setComment($comment);

Quite possibly, and this is just my personal preference, I don’t feel quite right injecting a service into the internals of a domain object. The powers that be have always proclaimed the domain layer must be agnostic about the service layer (unless you appeal to separated interfaces), and this one should lay down on an upper level interoperating with multiple clients. But in this case the approach isn’t really that sinful considering the Post class is just a dummy container for the registered observers which are only consumed when triggering event updates. Moreover, taking into account how neatly the responsibilities between the service in question and the Post class have been inverted, my complaint should be considered pretty much a picky whim.

Closing Thoughts

Quite often considered an obscure, tangled concept, especially in PHP where many developers tend to intuitively associate the concept only with plain old Dependency Injection, Inversion of Control is a simple yet killer programming methodology which, when properly implemented, is a fantastic way for creating decoupled, orthogonal systems whose components can be easily tested in isolation.

If you’re using Dependency Injection within your applications (you are, right?) then you should feel your coder’s instincts pretty well satisfied as you’re already exploiting the benefits that Inversion of Control provides. As I attempted to demonstrate before, however, there’s a wide array of situations where the approach fits well other than just managing class dependencies the right way. Event-Driven Design is certainly a good example.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Uncle Fred

    Looks like no one has discovered this gem yet.

    I have no problem with you labelling DI as a specialised implementation of IoC. Strange, but no other article of yours made me want to dig deeper into DI theory. And this article wasn’t even about DI!

    Love the use of SplObserver and SplSubject. Neat, really neat.

    • Alex Gervasio

      Hey Fred,
      As always, it’s really nice to hear from you again. Glad to see you enjoyed the post, and of course, thanks for dropping your insightful comments.

  • Petr

    Small question not directly related to IoC concept, but interesting for me in particular example. How to use several observers for different type of “events”. E.g. I want to have CommentsService to be notified when new comment is added and PostService to be notified when post is changed. Should I use something more complex that SplSubject and SplObserver or it’s possible to differentiate event types for different observers with these basic Spl interfaces?

    • Vince

      I am wondering the same as well. And I am thinking of one solution: attach and detach the services before and after the action they are associated to. Alex, would it be a solution?

  • A guy with a keyboard

    I think IoC and DI are two different things. They are the necessary steps (in that order) to achieve one goal of automating dependency management. First, you stop creating dependencies inside “client” classes. Instead, you declare dependencies in constructors. You were controlling your dependencies, now you have inverted the control to the outer context. You do the same thing for outer classes until you reach the top class(es). IoC step is done. All you need now is to provide (inject) each class its dependencies. You can do it manually or use a ready framework. Whichever you choose it would be DI.

  • Old dog

    So I enjoyed your post first off. I must admit though I am perplexed. I am a long time developer who came from VB and then spent most of the 2000s managing and now find myself back into the technical soup. Design patterns are interesting but I have no clue where to start to get up to speed on the most basic of concepts. If you have any suggestions it would be very appreciated.

    • http://www.web-brainz.co.uk/dip Martyn

      Hey “Old dog”,
      A very good book to get you into design patterns including why they are a good idea is: “Design Patterns Explained: A New Perspective on Object-Oriented Design”. (There’s a link on my web if click my name)
      Nice article Alex, Thanks.

  • More for the curious

    To “Old Dog” and others that are interested…

    If you’re curious about design patterns, such as this, there are three books that got me started. I had no idea what they were at first & quickly got really excited about them when I started reading about them. I got the first one because I wanted to get “good” at ActionScript. Once I started reading and getting a hint of “what” they can do, I became addicted — even though I had no OO experience and didn’t understand what a “class, abstract class, or concrete class” were at all. (I kept thinking of school! =b )khehehheeh

    The first three books I got, read, and highly recommend are:
    “Advanced ActionScript 3 with Design Patterns” Joey Lott & Danny Patterson:
    http://www.amazon.com/Advanced-ActionScript-3-Design-Patterns/dp/0321426568/
    “PHP Objects Patterns and Practices” Matt Zanstra:
    http://www.amazon.com/Objects-Patterns-Practice-Experts-Source/dp/143022925X/
    and
    “ActionScript 3.0 Design Patterns: Object Oriented Programming Techniques” William Sanders and Chandima Cumaranatunge:
    http://www.amazon.com/ActionScript-3-0-Design-Patterns-Programming/dp/0596528469/

    I didn’t really understand most of the the first time through. I did see their value & I “understood” the Decorator Pattern first. …and it’s still my favorite!

    Speaking of which, that gave me an idea that demonstrates the beauty/usefulness of patterns with respect to Petr’s question above.

    You could use the Decorator pattern to build your observers and then in the execution context you could “decorate” your observer (in any combination desired) before you “feed” it to the Post class. The decorator pattern would then work its magic and “pass the buck” down the chain of observers automatically.

    In short, the Decorator Pattern requires abstract decorated & decorator classes that you extend with concrete implementations. The decorators accept “decorators” & “decorated” objects in their constructor and “wrap” each new decorator around all the previous ones. So you could perform multiple observer actions as such:

    $post = new Post(...);
    $observer = new CommentService();
    $observer = new DbLoggerService( $observer );
    $observer = new CSVService( $observer );
    $observer = new RSSService( $observer );
    $post-&amp;gt;setObserver( $observer );
    //You could combine all the &quot;new&quot; into a single line too
    //e.g. $o = new A( new B( new C() ) );

    …and when Post::notify() executes, it will “chain” them in reverse order and issue an RSS update, CSV update, Database Log update, and finally dispatch the email. You can create as many “Services / Observers” as desired and combine them in any way, or order, you want in the executing script. They can even be dynamically combined at runtime using $_POST / $_GET, switches(), or any other control logic. As mentioned in the second book I listed, PEAR uses this pattern to build stream filters.

    Patterns are awesome! =D

    Many thanks for the post Alex.
    Todd

  • Alex

    Alejandro we miss you! Come back soon plz

  • Savas Vedova

    Really clean and structured post. I admire your work.