Previously I wrote:
Sometimes you work with something for so long that you forget that other people aren’t familiar with it. This happened recently talking to a friend of mine — “I’ve got no need for object orientation”, he said, and I was shocked. […] What, I wondered to myself, were the benefits of OO that he was missing? How could I persuade him that it does have a point? So here is my attempt to trawl my memory and see how I’ve changed in response to OO, and why I think it serves me (and my projects) better. In the end I’ve found that the benefits are real, but nothing I would have guessed at when I first started in this field.
I’ve already written about data hiding. Now read on…
Separation of concerns
This is one of those things that is a consequence of OO fundamentals, but by no means obvious. The root of so many problems in software is dependencies. A depends on B, B depends on C, and before you know it everything depends on everything else. It makes things very difficult to manage: changes become slower and slower, and riskier and riskier. Separation of concerns is about moving away from this: by forcing things into classes it encourages each class to do its own specific job. This breaks the dependencies and makes the management of your code much easier.
But it’s not that easy or obvious, and requires a fair bit of imagination to see it working really well. Let’s take a very simple example.
Suppose Guardian Unlimited has an Article class, and articles can be saved into a Repository (probably a database). Then we have two classes, each with a separate concern; the Article class has a save() method which uses the Respository to save to the backing store. But hold on there — we’ve still got the chance for it to go wrong.
An easy mistake would be to have the Article manage the setup of the Repository: when you create an Article it sets up the Repository all by itself. In some ways this is nice (it’s a form of encapsulation) because you can create and save an article without worrying about how the save is done, or even that there is a Respository at all. But there is a nasty dependency here: the Article depends on the Repository: not only are we unable to set up an Article without having a Respository, but it’s the Article which determines what the Repository is — if your application requires a working SQL Server database then it will always require a working SQL Server database, even if you just want to do a little test.
In our example the Article depends on the Repository. It also specifies exactly what the Repository is, because it sets it up internally. Dependency injection says we shouldn’t allow the Article to set up the Repository; rather, we should set up the Repository externally and inject it into the Article (for example by calling a setRepository() method). The dependency isn’t broken, but the dangerous element is: the particular kind of Repository is no longer a concern of the Article.
What this means in practice is that you can feed any kind of Repository into the Article and it will still work. You can even inject a “do nothing” Repository just in case you want to do a quick test which doesn’t require saving it into the real database. Or you could inject a disposable in-memory Repository which only lasts for a limited time.
All of this is possible with another feature of OO: abstraction. If we ensure Repository is only an abstraction (rather than a specific concrete class) then we can have all kinds of Repository implementations: a SQL Server one, an in-memory one, one which always throws an error (so you can check how your application handles errors), and so on. In VB.NET you’d use an interface for this particular example.
Dependency injection forces concerns to stay separate and makes your code much more flexible and easy to manage. But that wouldn’t be obvious to you if you’re just starting out in OO development, and if you’re just looking at the concept of objects, inheritance, and so on.
- Part I of V, data hiding
- Part II of V, separation of concerns (this installment)
- Part III of V, better testability
- Part IV of V, refactoring
- Part V of V, evolving the product