Monday, September 27, 2010

Dependency Injection Part 2: Loose coupling to the rescue

In my last post, we finally injected dependencies into our Magic8BallSimulator class, but soon realized that by injecting specific classes with specific behaviors we were coupling Magic8BallSimulator to its dependencies. We discussed how this kind of coupling would make our application brittle for the following reasons:
  1. If we want to change, say, ConsoleOutputService we risk breaking Magic8BallSimulator too, since it depends directly on ConsoleOutputService (and changing the output on the console really shouldn't break our entire application).

  2. If we get a requirements change to alter how we output messages, say, to send them as Instant Messages or via Email, we'll have to rip out ConsoleOutputService and change Magic8BallSimulator as well. This makes our application less maintainable because changes to one section break and cascade to other parts of our application making the whole thing a pain in the ass to work on.
Here's the Magic8BallSimulator constructor we ended up with last time:We decided that we needed to alter Magic8BallSimulator to depend upon abstractions instead of concrete implementations, making Magic8BallSimulator loosely coupled to the classes it depends on.

Consider our ConsoleOutputService class. It's a concrete class which has a specific behavior. It prints output to the console, and it's "to the console" behavior is rather specific don't you think? This means that when we inject ConsoleOutputService into Magic8BallSimulator, Magic8BallSimulator knows not only that ConsoleOutputService outputs things, but how it outputs them as well. Now how ConsoleOutputService outputs things is its own concern, and we'd like to keep our application flexible enough so we can change how we output things without without breaking Magic8BallSimulator. The solution is simple, we need to tell Magic8BallSimulator that we have a class capable of handling output, but not how that class does its job. Here's the solution to our output problem:

Interfaces!

IOutputService - a contract to provide output functionality
This bit is important:

IOutputService is a simple contract which says "I agree to provide the following behavior (PrintExit, PrintInputPrompt etc), but how I work inside is no one else's concern. Now we only need a simple modification to ConsoleOutputService to complete our de-coupling.

ConsoleOutputService - implements IOutputService and prints to the console
Let's modify Magic8BallSimulator's constructor to take its new de-coupled dependency (notice we're now passing in an IOutputService interface instead of an instance of the specific ConsoleOutputService class):2 more Interfaces

Let's de-couple Magic8BallSimulator from, the rest of its dependencies. Here are our remaining interfaces:
I won't bore you with pasting copies of ConsoleInputService implementing IInputService or of MessageService implementing IMessageService. If you'd like to have a look at those classes, I've posted them here. Stay with me, things are about to get interesting.

Success!

here's our new Magic8BallSimulator class: Magic8BallSimulator still has its dependencies injected, and behaves the way it did in the last post (in other words, we didn't break anything). Magic8BallSimulator still relies on the services provided by its dependent classes, but now it's de-coupled from the concrete details of how those classes do their work. Don't believe me?

Check this out

A really different implementation of IOutputService:
Yeah, that's certainly *different* :)
Here's the new implementation of IOutputService, check out PrintMessage(), it prints messages to the screen, a file, and within a Windows-forms MessageBox:What did we just do?

Well aside from writing a crazy output class, we've radically changed the output behavior of our application without breaking it! How? Simple. All Magic8BallSimulator knows is that it gets passed (or injected) an instance of a class which implements IOutputService, and our new (slightly odd) MultipleOutputService class does that.

Here's our new SimulationRunner class, it injects dependent classes into Magic8BallSimulator (remember, Magic8BallSimulator still sees these classes as Interfaces).Source code for this post (that really is an odd output class).

In the next post

We'll have a look at a Dependency Injection framework for .NET (there are quite a few to chose from) and how it can make our lives easier. We'll also discuss the differences between running our application and starting it up to run. We'll also check out a better (and more interesting) way of wiring in our dependencies to Magic8BallSimulator. >> Next Post

2 comments:

Punit said...

Hey, thanks for taking the time to write this. Its a concise example and gets the points across nicely!

However, i noticed a minor typo. in your code listing you intend to pass the IOutputService into the constructor, but you typed IMessageService instead as the third parameter.

So:
public Magic8BallSimulator(MessageService messageService, ConsoleInputService inputService,
IMessageService outputService)

should change to:

public Magic8BallSimulator(MessageService messageService, ConsoleInputService inputService,
IOutputService outputService)

Max said...

Thanks for catching that!