Saturday 17 April 2010

NUnit Part 4: Interfaces & Dependency Injection

At the end of my last post, I mentioned that we can test a classes behaviour using mocks, stubs and fakes. This is a huge help when testing code. But before we can use them we need to review a couple of (closely related) coding practices - interfaces and dependency injection.

In my experience, interfaces are easily one of the most overlooked aspects of C#. Many see them (as I once did, when I first learned Java) as rather pointless extra code. How wrong they are! Firstly, by designing to an interface, we are already creating a kind of test. We are guaranteeing that any code that implements the interface will provide all of its methods/properties. If they don't, we'll get compile time errors (which is a Good Thing&trade).

Another benefit of interfaces is a consequence of dependency injection (DI). Before I continue, a quick question: how often do your constructors do work (call methods/properties) on their arguments? Chances are your answer is "most of the time". But what's wrong with this? Firstly, it prevents us from using some of the most powerful tools we have when testing (see the next post). Secondly, we have no way of knowing what the constructor is actually using (and so we can create hidden dependancies - a Bad Thing&trade). Before you start complaining that passing in all our objects breaks encapsulation, consider the following constructors:


1. Foo(Bar bar1, Bar bar2, string name)...
2. Foo(FooSettings settings)
FooSettings(Bar bar1, Bar bar2, string name)


In the first instance, we can clearly see that Foo uses two Bars and a string. In the second we have no direct knowledge of the content - just that it takes an intermediate class from which it extracts what it might need. The FooSettings class is essentially redundant code, which introduces more room for bugs. DI means passing in all the objects we will need (we 'inject' what it 'depends on'). No big mystery to solve. The main advantage is we reduce coupling between classes, thus reducing the chance that an edit in one class will result in edits in many others. But what does this have to do with interfaces?

While a class in C# can only have one parent class, it can inherit from multiple interfaces. This means that it can be cast to any of the interfaces it implements. To understand this, you need to know a little about reference types. Classes and interfaces are both examples. Reference types differ from value types in how we store them in memory. When we call a reference types constructor, we actually create two different blocks of our RAM (if you want to get technical, one is on the heap while the other is on the stack). The first is the actual object - with all the state data it needs to work. The second is a pointer to the first block. It says where the state data is and what its type is. Thus, what methods can be run on the block. When we pass a reference type to a method, we simply create a new pointer block with its associated type. If, in the process, it is cast to another type, the new pointers type is changed appropriately. What does this mean and how does this help us? Time for a simple example.

Suppose we are making a dungeons and dragons style game. Every once in a while (at the end of each dungeon) we'll encounter some sort of boss. Lets call it a... hmm.... dragon? That'll do! Rather than creating a new dragon for each dungeon, we'll reuse the same instance over and over, changing its state as required (hit points, attack type, texture, name...). But we should restrict where its state can be set, to prevent it from accidentally being repeatedly given max health (or any other kind of unpleasant side effects).

To do this, we'll create a dungeon factory that exists for the lifetime of the game. Whenever we want a new dungeon, we'll ask the factory for one (supplying whatever arguments we need by DI). It will then create the dungeons layout, assign basic monsters etc... The factory will also store the single instance of the dragon class, which implements the IDragon interface. The factory can then call any reset methods (which aren't in the interface) on the dragon before passing a reference of it to the new dungeon. To prevent the dungeon from calling the reset methods, it stores an instance of the IDragon interface instead. Thus, even though the dungeon and the factory are pointing at the same object, the dungeon only know how to use the dragon - not reset it. Incidentally, we could apply the same principle to any of the dungeons objects - rooms, monsters, treasure etc...

If you're still confused you'll see this in action in a few posts time when we create our new keyboard class and interface for XNGen's input system.

To summarise, by implicitly casting a class to one of its interfaces when passing it as an argument, we can reduce the chance of errors occurring. But there is an additional benefit with regards to testing - creating mocks, stubs and fakes.

To be continued next post!

No comments:

Post a Comment