søndag den 9. februar 2014

SOLID principles, in layman's terms: Dependency Inversion

Raison d'être: I set out to write about the SOLID software development principles. Specifically, my aim was/is to make these things more understandable to other developers who, much in the way as yours truly, found it troublesome to have to decipher lengthy, complex articles and books on the matter. These principles are for everyone to learn and use, but I found that they were hard to fully grasp; quite possibly because I come from a non-English speaking background. So with this series of articles I'm setting out to try and de-mystify the principles, with only the best intentions in mind. The principles apply to many layers of software development. In my articles, I specifially aim to describe them as they relate to programming. I hope it'll be of use to you. Thank you for stopping by.

This will be a 5 article-series about SOLID. SOLID is all the rage; at least as far as the job-adds I'm reading are concerned; "you are expected to honor the SOLID principles" etc. So what exactly is SOLID about? Plain and simple, it's a set of guide-lines in developing object-oriented systems. They are a group of concepts that have proven themselves valuable for a great many people coding a great many pieces of software. A tale told by your elders in software engineering, if you will, that you will want to pay heed to so you can boast on your CV that you're into the SOLID principles - and you'll be a better developer for knowing them, I promise you that. Heck, you're a better developer for simply _wanting_ to know them!

SOLI[D] - The Dependency Inversion principle
No 5 and last round of our series of articles on the SOLID principles finds us at the Dependency Inversion principle. And as wikipedia says it so well, it [quote] "refers to a specific form of decoupling where conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are inverted (i.e. reversed) for the purpose of rendering high-level modules independent of the low-level module implementation details..[[/quote]. Begging your pardon? Let's try that again, in layman's terms:

The dependency inversion principle means that you'll give your classes the variables they'll need to function, as opposed to letting them declare those variables. Here's first an example of how to not follow the principle:

public class DiskLogLocation
{
    public DiskLogLocation()
    {
    }

    public void LogMessage(string message)
    {
        // do something to log the message here
    }
}

public class Logger
{
    private DiskLogLocation logLocation;

    public Logger()
    {
        logLocation = new DiskLogLocation();
    }

    public void Log(string message)
    {
        logLocation.LogMessage(message);
    }
}


public class Program
{
    private static Logger logger;

    static void Main(string[] args)
    {
        logger = new Logger();
        logger.Log("a log message");
    }
}

In the above basic we have a Program, which instantiates a Logger()-class, which in turn instantiates - "new's up", as some say - a DiskLogLocation object. This does not follow the Depedency Inversion principle, and in a beat I'll explain why. But here's first the right way to do it, if one wishes to follow the SOLID way:

public class DiskLogLocation
{
    public DiskLogLocation()
    {
    }

    public void LogMessage(string message)
    {
        // do something to log the message here
    }
}

public class Logger
{
    private DiskLogLocation logLocation;

    public Logger(DiskLogLocation _logLocation)
    {
        logLocation = _logLocation;
    }

    public void Log(string message)
    {
        logLocation.LogMessage(message);
    }
}


public class Program
{
    private static Logger logger;

    static void Main(string[] args)
    {
        DiskLogLocation diskLogLocation = new DiskLogLocation();
        logger = new Logger(diskLogLocation);
        logger.Log("a log message");
    }
}

The above code varies from the first in as much as we instantiate the DiskLogLocation object in our Main, and pass it on to our Logger-object right there as an argument in the constructor call. That is indeed 'inverted dependency', and that's what this Dependency Inversion principle is all about: Instead of having to depend on the Logger-class to declare and instantiate the DiskLogLocation object, we do it ourselves and simply pass it on. So it follows that instead of depending on our Logger-object to do it, we've "taken the responsibility back - inverted it back to Main()"

That, in a nutshell, is the Dependency Inversion principle. And the reason for wanting to apply it is this, that you'll gain greater control of how your classes are dependent on each other. We want to strive classes having as few dependencies as possible - that is, we want to limit to the extent possible having to 'new stuff up'. Why, because every time we do that, we introduce a dependency that we later cannot alter, as it's hard-wired into the class. But when we ourselves provide the class with the dependencies - in the above case the DiskLogLocation object - it needs, then we have achieved what's popularly referred to as 'loose coupling'; that is, we can easily change things around without having to re-wire a whole bunch of stuff. Well, at least that's the idea.

The above example is - of course - heavily simplified. The Dependency Inversion principle truly comes to the fore when we're dealing with abstractions, but that sentiment is grounded in the other SOLID principles and so I won't go into extending the example with that, rather refer to my other articles on those other principles mentioned.

You'll perhaps have heard of the principle of Dependency Inversion in one of its various guises: Dependency Injection, Inversion of Control, what have you. It's all about the same thing, really, if you understand the basic premise as described in the above, you should be in the clear. If you're wondering how go to from here, there's absolutely no way around researching 'Dependency Injection Containers', or DI Containers for short. A bunch of very clever people have sat down and implemented some equally clever components that takes away all the rough work, so you can invert your dependencies to your heart's content. Some popular ones include Ninject, Castle Windsor, AutoFac; I'll venture to specifically recommend the former, Ninject.

So there you have it, the Dependency Inversion principle; don't let your classes decide which dependencies they need, decide for them and that's all they damn well get. In turn, you offer thoses classes - and your project alike - the possibility to replace those dependencies with something better, should it come along. And in an ever-changing IT climate that's pure gold, the change to adapt quickly to change.




Hope this helps you in your further career. And good luck with it, too!

Buy me a coffeeBuy me a coffee