Dependency injection

In the previous chapter we briefly already discussed dependency injection (DI). Now we will dig into it a bit more detail.

Objects usually do not work on their own. Most of the time the implementation depends on the services of other classes. When we want to write something to the console we use the System class. When we manage the table of guesses we need Color objects and ColorManager.

In case of writing to the console we may not realize the dependency because the class being part of the JDK class library is available all the time and all we need to do is to write System.out.println. In this case this dependency is wired into the code. We cannot send the output somewhere else unless we change the code. This is not too flexible and in many cases we need a solution that can work with different output, different color manager or different whatever service our code depends on. The first step to do that is to have a field that has a reference of the object that gives our class the service. In case of output the type of the field can be of type OutputStream. The next, more interesting step is how this field gets value.

One of the solution is to use DI. In this approach some external code prepares the dependencies and injects them into the object. When the first call to a method of the class is issued all the dependencies are already filled and ready to be used.

In this structure, we have four different players:

  • The client object is the one that gets the injected service objects during the process
  • Service object or objects are injected into the client object
  • Injector is the code that performs the injection
  • Interfaces define the service that the client needs

If we move the logic of the creation of the service objects from the client code the code becomes shorter and cleaner. The actual competency of the client class should hardly ever cover the creation of the service objects. For example a Game class contains a Table instance but a game is not responsible to create the Table. It is given to it to work with it, just as in real life that we model.

The creation of service objects is sometimes as simple as issuing the new operator. Sometimes service objects also depend on other service objects and that way also act as clients in the process of dependency injection. In this case the creation of the service objects may be a lot of lines. The structure of the dependencies can be expressed in a declarative fashion that describes which service object needs which other service objects and also what implementation of the service interfaces are to be used. Dependency injection injectors work with such declarative descriptions. When there is a need for an object that needs service objects that themselves need again other service objects the injector creates the service instances in the appropriate order using the implementations that are matching the declarative descriptions. The injector discovers all the dependencies transitively and creates a transitive closure graph of the dependencies.

The declarative description of the needed dependencies can be XML, or a special language developed especially for the dependency injection or it can even be Java itself using specially designed fluent API (https://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course/). XML was first used in DI injectors. Later Groovy based Domain Specific Language (https://martinfowler.com/books/dsl.html) came into picture and Java fluent API approach. We will use only the last one being the most modern and we will use Spring and GuiceDI containers since they are the most well-known injector implementations.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.167.195