Dependency Injection Analogy

A Dependency Injection Analogy

There are many articles already discussing the details of various dependency injection implementations in a C# context. There's no point me writing another one. What I want to add is an analogy that helps you understand the benefits of DI. It's not always clear how DI adds to the efficiency of what you're coding, hopefully this will help a bit by showing you what you would end up prior to introducing DI.

Let’s start with the following class:

  1. public class CordlessDrill  
  2. {  
  3.    public void Drill()  
  4.    {  
  5.       var drillBit = new DrillBit();  
  6.       drillBit.Turn();  
  7.    }  
  8. }  
The cordless drill we just defined has the ability to drill things, but it depends on a drill bit to do so (obviously). Therefore, we instantiate a drill bit, and we turn it by calling Turn on the new drill bit object. These classes are thought of as ‘tightly-coupled’, as the cordless drill class directly depends on the concretion of a DrillBit object. All good so far, the drill does the job.

But now we’ve come across a different task that needs doing; we need to saw wood. Well the class we’ve just wrote is useless to us, so we create another class – another tool to do this particular job:
  1. public class CordlessSaw    
  2. {    
  3.    public void Saw()    
  4.    {    
  5.       var sawBlade = new SawBlade();    
  6.       sawBlade.Saw();    
  7.    }    
  8. }   
Again, a class tightly-coupled with another class, designed to do a specific job.

We’ve got enough tools to do both the jobs so far, but as we tackle new tasks and their requirements, we’ll need new classes. It’s inefficient to keep writing these ‘cordless’ classes (i.e. carry around as many tools as there are tasks), when in fact they share some common functionality. And that’s when it’s time for dependency injection.

If we disregard the actual details (the ‘implementation’) of the power tools dependency (i.e. whether it’s a saw or a drill bit), we can focus on the fact that it needs an attachment of some sort and that it will provide power to that attachment – that it is the only job. At this point, what does being this vague allow us to do? It allows us to define the attachment just as an attachment; an abstraction, rather than a concrete implementation. We could translate this, in real terms, to “I know the tool needs an attachment, I don’t care whether it’s a drill bit or saw blade”. Our code can then start to look like this:
  1. public interface IAttachment  
  2. {  
  3.     void DoTask();  
  4. }  
  6. public class DrillBit: IAttachment  
  7. {  
  8.     void DoTask()  
  9.     {  
  10.         // drill stuff    
  11.     }  
  12. }  
  14. public class SawBlade: IAttachment  
  15. {  
  16.     void DoTask()  
  17.     {  
  18.         // saw stuff    
  19.     }  
  20. }  
  22. public class CordlessTool  
  23. {  
  24.     private IAttachment _attachment;  
  26.     public CordlessTool(IAttachment attachment)  
  27.     {  
  28.         _attachment = attachment;  
  29.     }  
  31.     public void GivePowerToAttachment()  
  32.     {  
  33.         _attachment.DoTask();  
  34.     }  
  35. }  
It’s clear to see the benefits of what we’ve just done. We’ve got a single class now responsible for giving power to the attachments, and doing only that. It’s more maintainable, less cumbersome, it’s easily extendible and last, but not least it can be unit tested without a regard for what IAttachment actually does as part of DoTask() (see ‘mocking’).

There are a few things that haven’t been covered here, as they have been well covered in the other articles I mentioned earlier. For example, which IAttachment is injected into the CordlessTool class is a decision that does need making at some point. Typically, this is done with an inversion of control container. Also, we’ve opted for constructor injection here – as it mostly suits the requirements of this example. There are other ways of injecting dependencies.

Similar Articles