Stefan Forsberg
Jun 10, 2010
  2237
(1 votes)

Design principles and testing – part 3

Let’s return to our weather data from part 1. In it’s current state it looks something like this

WeatherDataClasses

So the weather service is using YrNoWeatherRepository to load some xml and then a mapper to map from that xml to properties on the WeatherData class. Let’s ignore the mapper for a second and talk about the repository and it’s relation to the service.

If we look at the code for the service it’s fairly obvious that it has a dependency to repository in a way that you can’t create a WeatherService class without also creating an instance of the repository class.

 

So what’s the problem with this?

Imagine that we want to change our repository implementation to some other weather service. Since our weather service in it’s current state not only care about the ability to fetch weather data but also finding which class and how it handles that, we have to edit our service to change the implementation.

And our new friend Dependency Inversion Principle (DIP) would not like that, not one bit. Like this motivational picture so elegantly states, designed applications this way is like soldering a lamp directly to the electrical wiring in the wall. I think most would agree that it would make more sense if you (the user of the electrical wiring) was responsible for plugging something in instead.

DIP states that “High-level modules should not depend on low-level modules. Both should depend on abstractions.” Another way to put it is that you should program to an interface and not an implementation. (Note here that “interface” in that sentence talks about the concept of interface, not the c# type interface (eg an abstract class can also be an interface).)

So let’s try to break away our high-level (WeatherService) object dependency to the lower level (YrNoWeatherRepository) one.

 

Refactoring to better comply to DIP

We’ll start with extracting an interface from YrNoWeatherRepository which it will implement itself.

   1: public interface IWeatherRepository
   2: {
   3:     XElement Load();
   4: }
   1: public class YrNoWeatherRepository : IWeatherRepository
   2: {
   3:     public XElement Load()
   4:     {
   5:         return XElement.Load("http://www.yr.no/place/Sweden/Stockholm/Stockholm/forecast.xml");
   6:     }
   7: }

The point here is that our service should only program to that interface. So how do we make that happen?

   1: public WeatherData GetWeatherData()
   2: {
   3:     IWeatherRepository repository = new YrNoWeatherRepository();
   4:     return WeatherDataMapper.FromYrNoXml(repository.Load());
   5: }

This doesn’t give us much, sure we program against the interface rather than a concrete implementation, but it’s still as dependent on the concrete class as it was when we begun.

 

Dependency injection (DI) and Inversion of Control (IoC)

What we want to achieve is that the service class shouldn’t be responsible for resolving it’s dependencies but rather being told about them. This is what Dependency Injection is all about. You might also hear people talk about the Hollywood principle (“don’t call us, we’ll call you”). So in essence what this means is that we should tell the service which concrete implementation to use rather then letting it decide for itself. In a way this is the complete opposite to how you normally design your application (or at least it was when it was introduced in 1988) which is why it’s called Inversion of Control (ioC). You could say that DI is a way to achieve IoC.

 

Injecting dependencies

The first (and I think most widely used) approach is to inject the dependencies in the objects constructor, called Constructor injection.

   1: private readonly IWeatherRepository weatherRepository;
   2:  
   3: public WeatherService(IWeatherRepository weatherRepository)
   4: {
   5:     this.weatherRepository = weatherRepository;
   6: }
   7:  
   8: public WeatherData GetWeatherData()
   9: {
  10:     return WeatherDataMapper.FromYrNoXml(weatherRepository.Load());
  11: }

Notice that the GetWeatherData method only talks to the repository field which is of the type IWeatherRepository.

If we try to build this we’ll get errors from all the places that currently uses the WeatherSerivce since we by doing this removed the default constructor which all current code uses. To solve this there are (at least) three approaches of which we’ll speak of two now and one later on.

 

1. Poor mans dependency injection

The quickest way is to simply add a default constructor that passes in the concrete class we want to use

   1: public WeatherService() : this(new YrNoWeatherRepository())
   2: {
   3: }
   4:  
   5: public WeatherService(IWeatherRepository weatherRepository)
   6: {
   7:     this.weatherRepository = weatherRepository;
   8: }

As you notice this leads to our class being as dependent to the concrete class as it was when we begun. This is called poor mans dependency injection for this reason and some even consider it somewhat of an anti-pattern. But it can be a real life-save (or at least the only viable option) when dealing with a legacy app that you’re rewriting to (for instance) make more testable.

 

2. Using a factory

The other option is to remove the default constructor and change every occurrence to inject the concrete class like such

   1: new WeatherService(new YrNoWeatherRepository())

If we have a lot of these in our code base it can be somewhat of a pain to update them (even with ReSharper). Plus if we change which implementation to use we have to once again edit all the occurrences. One thing we could to is to hide the initiation behind a factory (yes I remember that I said no design patterns…)

   1: public class ServicerFactory
   2: {
   3:     public static WeatherService GetWeatherService()
   4:     {
   5:         return new WeatherService(new YrNoWeatherRepository());
   6:     }
   7: }

And when we want a service we simply call our ServiceFactory

   1: var weatherData = ServiceFactory.GetWeatherService().GetWeatherData();

 

So what about the third option? And when is this “testing” starting?

The third option will be discussed in next post where we’ll also talk more about what the benefits of doing this are, what an IoC-container can do for us and then (finally) start testing our application. Cliffhanger alert!

If you feel this series doesn’t really make sense, have questions or ideas about what it also should mention or have any other feedback please leave a comment or drop me a mail at stefan.forsberg at cloudnine . se.

Jun 10, 2010

Comments

Sep 21, 2010 10:33 AM

They sure do make sense :) Nice writing and presented in an easy to understand fashion. I think a lot of people will enjoy these posts, I know I do :)

Keep up the good work!
/ Bjørn Isaksen

Sep 21, 2010 10:33 AM

Thanks for your kind words! =)
/ Stefan Forsberg

Please login to comment.
Latest blogs
Creating an Optimizely CMS Addon - Adding an Editor Interface Gadget

In   Part One   of this series, I covered getting started with creating your own AddOn for Optimizely CMS 12. This covered what I consider to be an...

Mark Stott | Aug 30, 2024

Configure your own Search & Navigation timeouts

The main blog Configure your own Search & Navigation timeouts was posted for years but you need copy the code to your application. We now bring tho...

Manh Nguyen | Aug 30, 2024

Joining the Optimizely MVP Program

Andy Blyth has been honoured as an Optimizely MVP, recognising his contributions to the Optimizely community. Learn how this achievement will enhan...

Andy Blyth | Aug 29, 2024 | Syndicated blog

Welcome 2024 Summer OMVPs

Hello, Optimizely community! We are thrilled to announce and welcome the newest members to the Optimizely Most Valuable Professionals (OMVP) progra...

Patrick Lam | Aug 29, 2024

Create custom folder type in Edit Mode

Content Folders, which are located in assets pane gadget or multichannel content gadget, allow you to add any type of block or folders. But...

Grzegorz Wiecheć | Aug 28, 2024 | Syndicated blog

Creating an Optimizely AddOn - Getting Started

When Optimizely CMS 12 was launched in the summer of 2021, I created my first AddOn for Optimizely CMS and talked about some of the lessons I learn...

Mark Stott | Aug 28, 2024