The Dependency Inversion Principle states:
- A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
- B. Abstractions should not depend upon details. Details should depend upon abstractions.
<br /> (Source WikiPedia).
Throw that at a terrible programmer, and all you’ll get is a terrible programmer that is annoyed and hates you. So true!
Take the following method:
public void PrintHelloMessage()
{
Console.WriteLine("Hello");
}
Now ask a developer to add a new method to print the message ‘Goodbye’. Do you think he would do:
public void PrintGoodbyeMessage() { Console.WriteLine("Goodbye"); }
or:
<span class="kwrd">public</span> <span class="kwrd">void</span> PrintMessage(<span class="kwrd">string</span> message)<br />
{ Console.WriteLine(message); }
Most likely, he’d do the latter. Why? Because he realizes he’s gaining a benefit by passing a parameter to a method. He knows that if tomorrow you ask him for a “Good Afternoon” message, he won’t have to write a new method.
What is Dependency Injection? It’s one way of complying with the Dependency Inversion Principle. However, when you think about it, what does it boil down to? Passing a parameter to a method, which happens to be a constructor. It seems simple enough doesn’t it? Yet, it’s hard for people to understand it. Why? because they don’t see the value in it.
Explaining a principle to someone without them understanding the benefits and values they get out of it is useless, and that is why concepts such as Dependency Injection or Inversion of Control seem overly complex to the vast majority of developers (believe it or not, those of us that use these things are still a very big minority). It’s complex because they haven’t been explained the values of it. They’ve just been thrown some definition and they are expected to understand that it’s bad for one class to create an instance of another class it uses.
Present a developer with the following code:
public class AuthServices
{
public void AuthUser(string username, string password)
{
var authDAL = new AuthDAL();
var user = authDAL.GetUserByUsername(username);
if (user != null)
{
if ...
}
}
}
and ask them how they’d go about testing this code without having access to a database. Ask them how they’d go about changing AuthDAL for some fake DAL that doesn’t really connect to a database.
They’ll probably come up with the solution of passing the AuthDAL class in as a parameter, and eventually realizing that multiple methods will use the same class, they’ll pass it in via the constructor and set it as an instance field. As long as their AuthDAL has virtual methods, they can create any fake DAL that overrides those methods and returns some dummy value. They might argue that they don’t want virtual methods. And that’s fine. Tell them to define the parameter as an interface. In fact, they probably have already heard of a principle that says that you should program to an interface and not a class. They’ll eventually end up with this code:
public class AuthServices { IAuthDAL authDAL; public AuthServices(IAuthDAL authDAL) { _authDAL = authDAL; } public void AuthUser(string username, string password) { var user = _authDAL.GetUserByUsername(username); if (user != null) { if ... } } }
And voila! You have Dependency Injection via Constructor.
Once they *get* that, then explain to them other benefits, you know the real benefits they get from doing this: decoupled code, easy maintenance, promotion of SRP, etc. and then throw the principle in their face:
- A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
- B. Abstractions should not depend upon details. Details should depend upon abstractions.
And they’ll see how it all makes sense.
It’s not about throwing or not throwing books. It’s about showing people how something can help them, how they get value of it.