Easy to do in .NET with a generic Dictionary
- the entry point of the code that uses the business layer (an executable for instance). This is production code and it has to create the object connecting to the real database.
int _tmain(int argc, _TCHAR* argv[])
{
// Create the concrete DataLayer object
DataLayer dataLayer;
// Register it with the resolver
Resolver::Register<IDataLayer>(dataLayer);
BusinessStuff businessStuff;
businessStuff.DoStuff();
return 0;
}
{
// Create the concrete DataLayer object
DataLayer dataLayer;
// Register it with the resolver
Resolver::Register<IDataLayer>(dataLayer);
BusinessStuff businessStuff;
businessStuff.DoStuff();
return 0;
}
- the unit-test code. The IDataLayer object in that case is a fake object that doesn't connect to the real database and therefore makes unit-tests easily reproducible and fast.
void BusinessStuffTest::DoStuff_Nominal_DoesNotRaiseException()
{
// Arrange
// Create the concrete DataLayer object
FakeDataLayer dataLayer;
// Register it with the resolver
Resolver::Register<IDataLayer>(dataLayer);
BusinessStuff businessStuff;
// Act
businessStuff.DoStuff();
// Assert
dataLayer.AssertStuffWasDone();
}
{
// Arrange
// Create the concrete DataLayer object
FakeDataLayer dataLayer;
// Register it with the resolver
Resolver::Register<IDataLayer>(dataLayer);
BusinessStuff businessStuff;
// Act
businessStuff.DoStuff();
// Assert
dataLayer.AssertStuffWasDone();
}
The Resolver class has two methods Register and Resolve. Here is the code for the Resolver class (very straightforward):
#pragma once
#include
#include
using namespace std;
///
/// Allows you to Register and Resolve global objects
///
class Resolver
{
static map<string, void* > typeInstanceMap;
public:
template<class T> static void Register(const T& object)
{
typeInstanceMap[typeid(T).name()] = (void*)&object;
}
template<class T> static T& Resolve()
{
return *((T*)typeInstanceMap[typeid(T).name()]);
}
};
#include
#include
using namespace std;
///
/// Allows you to Register and Resolve global objects
///
class Resolver
{
static map<string, void* > typeInstanceMap;
public:
template<class T> static void Register(const T& object)
{
typeInstanceMap[typeid(T).name()] = (void*)&object;
}
template<class T> static T& Resolve()
{
return *((T*)typeInstanceMap[typeid(T).name()]);
}
};
This is a just a starting point. In production Resolver could do with methods such as Unregister(), IsRegistered(), UnregisterAll()...
An IoC container is one way to do dependency injection. Other ways include:
- pass the injected object using the constructor
- pass the injected object using a setter method
I tried all 3 methods and I find the IoC approach scales rather well. I've used it for 10 months in a production project and it works nicely with unit tests.
Resources:
Jean-Paul Boodhoo on the gateway pattern
Roll your own IoC container (dnrTV)
The Art of Unit-Testing where Roy Osherove tells everything about dependency injection and fake objects.
6 comments:
Check out the code I'm working on for just this very thing: http://bitbucket.org/cheez/dicpp/wiki/Home
Looks pretty similar to yours but has a bit more features.
> It's one way to do dependency injection.
This is wrong. If your only purpose is to do DI/IoC, you should not use an IoC container. Just like mount a wheel on a bike is not the cheapest way to spin it.
IoC container isn't to do DI/IoC but merely used the design/principle of IoC. In another words, it is not IoC container facilities DI/IoC, but DI/IoC enables IoC container. This is similar to that old EJB (2.x) containers were not to do or to help service lookup (a hash table would be much cheap than an EJB container then) but simply used service lookup. A bike isn't to spin wheels, it uses wheels....
No, just me being dumb.
m_dataLayer needs to be a reference, not a pointer and resolver needs the type as the template parameter.
public:
BusinessStuff() : m_dataLayer(Resolver::Resolve()) { }
private:
IDataLayer& m_dataLayer;
Thanks grahamr! I'll check and fix that as soon as I get the chance
Inspired by your article I have did my own, see here:
http://marek77.wordpress.com/2011/09/07/basic-ioc-inversion-of-control-container-for-c/
Post a Comment