As developers we are always trying to find ways to make our applications more flexible and tolerant of change. Ideally, we want our components to be modular and reusable. The ability to unit test our code is also becoming a requirement in more and more software development projects.
A technique that can help us in both of those areas is Dependency Injection or DI. DI is a simple pattern where we take elements of our class that would normally be treated as static dependencies and instead we provide an implementation of that dependency at runtime. Consider the following snippet of C# code:
In this case MyDomainClass has a dependency on some sort of data repository. As you can see on line seven, our class is expecting to use a specific implementation of this dependency. While this is something you’ve no doubt seen in many demos, training materials and even in production code, it’s actually not a good idea for several reasons:
- By declaring our variable of the specific type OracleRepository we are bound to that implementation and have limited the reusability of this code to only situations where Oracle is being used to store data.
- When we combine the instantiation with the declaration in the class, we have no opportunity to vary what’s being provided as our repository at runtime
- Static dependencies make unit testing more difficult
Refactoring this to use DI involves two steps. First, I would prefer it if MyDomainClass was dependent on an abstraction of a data repository instead of a specific data repository. This can be accomplished by creating an interface called IDataRepository that serves as an abstraction for a generic data repository and provides definitions of the methods needed to interact with such a repository:
Next, we want OracleRepository to implement the IDataRepository interface:
By doing this we are saying that OracleRepository supports the abstraction represented by IDataRepository and will provide the methods defined in IDataRepository. This means that OracleRepository can be used anywhere my application expects an instance of IDataRepository, such as the instance variable definition in MyDomainClass (line 7):
Now MyDomainClass is a lot more flexible. It can use any data repository that implements the IDataRepository interface. This opens us up to using other types of databases like MS SQL Server or MySql. It even enables us to use things like document databases, file systems or proxies representing web services. As long as the methods in IDataRepository are implemented, MyDomainClass can use it.
But we still have the problem of being statically bound to OracleRepository. This is due to the combination of declaration and instantiation on line seven. This is where DI comes into play. Instead of being statically bound to an implementation of OracleRepository, we are going to “inject” an implementation of IDataRepository at runtime. The easiest way to accomplish this is with “constructor injection” which is a pattern where an implementation
of IDataRepository is provided as a constructor parameter at runtime:
Now we no longer have a static dependency on one type of data store provider. Instead we are dependent on an abstract, for which we will be provided a concrete implementation at runtime. This provides several benefits:
- We can leverage other types of data stores, so long as their repositories can support the IDataRepository interface
- MyDomainClass becomes more reusable as it’s no longer dependent on OracleRepository
- Unit testing is easier as we can provide amocked instance of IDataRepository instead of a concrete implementation.
I hope this post has helped you understand DI a little more. It’s definitely a great pattern to adopt in your software development practices.