Wednesday, June 22, 2011

What is the Inversion of Control Principle?

Inversion of Control and Dependency Injection, IoC and DI. You hear them so often together in day-to-day lingo that it has become hard to separate them.

But they're not the same thing! DI is a specific form of the abstract IoC principle. So in this article I want to discuss and briefly explain IoC without going into DI. In another post I discuss IoC Containers, DI, and how to use the IoC/DI pattern to create clean, testable code.

But for now, what is IoC? To explain i'm going to very briefly jump back to basics - to explain what it is not.

Traditional Control of Flow
When I learned to program console applications, I learned to write functions (this is C-style pseudocode):

void printName(string name){
    print(name);
}

string readName(){
    print("What is your name?");
    string name = readLine();
    return name; 
}

... and to execute those functions from an execution-path entry point, i.e.:

void main(){
    string name = readName();
    printName(name);
}

If you expand much further from this simple example it quickly becomes clear that there is a central 'trunk' of execution (i.e. main()). This central trunk is flanked by large amounts of supporting code, in the form of functions (which in turn call more functions). All of the power to control program flow resides in the central trunk.

When we move on to OO, the functions became wrapped in classes, but the principle of centralised program flow control remained.

public class BusinessLogic {

    private string name;

    public void Run(){
        name = this.ReadName();
        Console.WriteLine(name);
    }

    private string ReadName(){
        Console.WriteLine("Please enter your name\n");
        name = Console.ReadLine();
    }
}

public class Program() {
    public static void Main() {
        BusinessLogic busLogic = new BusinessLogic();
        busLogic.Run();
    }
}

Ofcourse, with OO you end up with many more layers - your main Program class will instantiate objects and those objects will instantiate objects. But what we ended up with was a cluster of classes in the middle that control program flow, and you can draw a single line of execution through them. The classes in this cluster hand off tasks to a variety of library-style components (other classes, or 3rd-party libraries):

public class BusinessLogic {

    private string name;
    private CaseLibrary lib = new CaseLibrary();

    public void Run(){
        name = this.ReadName();
        name = this.UpperCase(name);
        this.PrintName();
    }

    private string ReadName(){
        Console.WriteLine("Please enter your name\n");
        name = Console.ReadLine();
    }

    private string UpperCase(string name){
        name = lib.UpperCase(name);
        Console.WriteLine(name);
    }
}

But libraries have very little freedom to control program flow. They can do what they like in responding to your requests, but they are little more than calculators, waiting for your application to give them some values and a specific task to do. They do the task and then return flow control to your central trunk.

So What is Inversion of Control?
IoC is all about handing control of program flow to somewhere other than the central trunk. It's a mainstream concept and the central principle of many event-driven architectures. IoC is commonly found in GUI applications, so in this example we'll use a framework to implement a GUI. This will mean handing over control to the (fictional) GuiFramework:

public class BusinessLogic {

    private string name;
    private GuiFramework gui = new GuiFramework();

    public void Run(){
        gui.NamePrompt("Please enter your name");
        gui.AddNameChangedHandler(OnNameChanged);
    }

    public void OnNameChanged(object sender, NameEventArgs e){
        name = e.Name;
        Console.WriteLine(name);
    }
}

Note that our application logic is still able to react to events in the GUI. This is done by assigning an EventHandler to the relevant framework object as a listener for NameChanged events. We hand over flow control to the framework, but we are able to insert our own code to respond to certain specific events. In this way we can customise the framework, but control is clearly inverted. Hence, Inversion of Control.

The Hollywood Principle
It's the oft-quoted phrase of IoC: "Don't call us, we'll call you." Once you hand over control to another component, you don't need to badger it with requests, or re-assume control. You just set up a listener and wait for the framework to call you back. You retain just enough code to handle your business logic, no more, no less.

So when using IoC with a number of framework-style components, control of the program is spread, and each component takes care of it's own responsibilities. Which is good, because a GUI framework knows more about how to render a GUI than does my application logic.

Frameworks vs. Libraries
As Martin Fowler points out, the IoC principle gives us a clear way to distinguish between libraries and frameworks:

"Inversion of Control is a key part of what makes a framework different to a library. A library is essentially a set of functions that you can call, these days usually organized into classes. Each call does some work and returns control to the client. A framework embodies some abstract design, with more behavior built in. In order to use it you need to insert your behavior into various places in the framework either by subclassing or by plugging in your own classes. The framework's code then calls your code at these points."

For more info, see Martin Fowler's article on Inversion of Control.

Or you can jump ahead to my article on IoC/DI Containers, and learn about the IoC/DI pattern.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.