In part 2 of this tutorial, we moved the decision logic out of the Game class into the Decision Engine class to separate concerns. Now we introduce dependency injection.
In this part of the tutorial, we will use dependency injection (DI) to improve the design of the code base. Zby provides a useful definition of dependency injection: “Dependency injection means giving an object its instance variables instead of letting it create them”.
In our case, we will be giving the Game object an instance of Decision Engine instead of letting the Game object create it itself. We will do this via the Game’s constructor, however it could be done though a property or a method. These alternatives are known as constructor injection, property injection and method injection respectively.
This is the essence of the dependency injection pattern. Confusion tends to creep in when a dependency injection container is introduced. DI containers, such as unity or Castle Windsor, provide mechanisms to aid in implementing the dependency injection pattern. However, they are not required to implement the pattern. I recommend the use of DI containers when the benefit of using one outweighs the complexity that is introduced by using it.
For the sake of simplicity, I will be using the dependency injection pattern and will avoid dependency injection containers for the time being. Returning to the rock, paper scissors example, DI will provide two main benefits:
- Flexibility – we will be able to provide different implementations of the Decision Engine without changing the Game class. This ‘pluggable architecture’ adheres to the open close principle. An example of this would be a decision engine that not only decides on a result, but also logs the results to a data store. I will return to this should time allow.
- Testability – during testing, we will be able to pass a mock object to the Game object, thus allowing us to test the Game class in isolation of the Decision Engine. This will be demonstrated in the code below.