This article is the second part of a series of articles, which, according to the author, are intended for those who cannot understand the implementation of dependencies and the Dagger 2 framework, or are only going to do it. The original was written on November 25, 2017. Images and GIF - from the original. Free translation.
This is the second article in the series
"Dagger 2 for novice Android developers." . If you have not read the first, then you
here .
Article series
Earlier in the series of articles
In the last article, we discussed dependencies. We learned what dependency is, discussed their types, the impact on the development process and the sustainability of the project.
')
What is dependency injection (dependency injection, DI) ?
Earlier we understood what dependency is and how it affects the project. Now let's look at what dependency injection is.
Before disassembling dependency injection, we need to understand how to avoid the trap in which we will be surrounded by dependencies. The problem of strong connections
(hard dependency) as White Walkers
(White Walkers) . We should not join their army; on the contrary, we must find a way to defeat them.
The strategy of solving the problem of strong connections (hard dependency) or the problem of White Walkers
We need a clear plan to solve or prevent the problem of strong ties. This strategy or plan is called
dependency injection (DI) . In other words, to kill White Walker you need to burn him or use dragon-glass weapons. Similarly, to avoid strong links, you must use dependency injection.
Dependency injection is just one of the methods to prevent strong connections. There are many other ways. For Android development, this method is considered the easiest and many large-scale projects use a dependency injection strategy.
Dependency injection method
Dependency injection is a method in which one object provides the dependencies of another object. Dependency
(dependency) is an object that we can use (
service ). An
injection (injection) is the transfer of a dependency to a dependent object (
client ) that will use this dependency. Service is part
of the client’s
state . To transfer a service to a client, instead of allowing the client to create or
find a service, is the basic requirement of the
“Dependency Injection” (DI) design pattern.
Compare this with the Game of Thrones. Cersei is preparing for a big war. Instead of burning all the money in her kingdom, she is trying to get a loan from the iron bank of Braavos. In this situation, money is addiction. All homes need money to wage war. Thus, an external object (iron bank) will introduce dependency (money) to dependent objects (houses).
In other words, dependency injection is based on the
concept of inversion of control , which suggests that a class should receive its dependencies from the outside. Simply put, no class should create an instance of another class, but should receive all instances from the configuration class.
Example 1
Stop talking. Let's disassemble the code. Consider a small example with two scenarios. In the first scenario, we will create several classes with
hard connections
(hard dependencies) , that is, without using dependency injection. Then, following the pattern of “dependency injection”, we will get rid of strong ties.
Scenario 1. Without using dependency injection
Problem: The battle of bastards -
Starks and Boltons are preparing for war to seize the North. We need to prepare them and bring them to war.
Since the example can include many houses, let's create a common interface
House
, include the methods
prepareForWar()
and
reportForWar()
.
public interface House { void prepareForWar(); void reportForWar(); }
Next, create classes for the
Starks
and
Boltons
. Classes will implement the interface
House
.
public class Starks implements House { @Override public void prepareForWar() {
public class Boltons implements House { @Override public void prepareForWar() {
Note: this.getClass().getSimpleName()
simply returns the class nameNext you need to bring both houses to war. Let's create a
War
class and ask both houses to prepare for the war and report it.
public class War { private Starks starks; private Boltons boltons; public War(){ starks = new Starks(); boltons = new Boltons(); starks.prepareForWar(); starks.reportForWar(); boltons.prepareForWar(); boltons.reportForWar(); } }
Analyzing War
Consider the constructor class
War
. He needs two classes
Starks
and
Boltons
. These classes are created inside the constructor, prepare for war and report on it.
In a well-designed object-oriented application, each object has a minimum number of responsibilities, the objects rely on others to do most of the work. In this example, the
War
class depends on the
Starks
and
Boltons
. These are
War
class dependencies.
War
will not work without them. Before a class starts performing real functions, all its dependencies must be satisfied in some way. The dependencies of the
War
class in this example are satisfied by creating instances of the classes in the constructor.
Although the option of creating dependencies inside a class constructor works quite well in small applications - it introduces disadvantages as the project grows.
First, the class becomes rather inflexible. If the application should work on multiple platforms or in several modes, for example, you need to replace the
Boltons
class with another one or split it into several objects. Make it becomes not so easy.
Secondly, it is impossible to test these classes in isolation. Creating an instance of
War
automatically creates two other objects that will ultimately be tested along with the class
War
. This can be a problem if one of the objects depends on an expensive external resource, for example,
Allies
(allies) or one of the objects itself depends on many others.
As mentioned earlier, fighting
hard dependencies is like fighting White Walkers without the proper weapons.
Summary
In this article, we looked at dependencies and dependency injection in detail. No class should create an instance of another class, but should receive all instances from the configuration class.
Dependency injection is a method for solving or preventing strong linkage problems. By providing dependencies from the outside, this method makes the class independent, testable, and supported.
We also looked at the
hard dependencies example when creating the bastard battle scenario.
What's next?
In the next article, we will implement the
War
class using the "Add dependency" template with the traditional approach.