📜 ⬆️ ⬇️

Dagger 2 for novice Android developers. The introduction of dependencies. Part 1

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.

image

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.

media


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.

media


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() { //-  System.out.println(this.getClass().getSimpleName()+" prepared for war"); } @Override public void reportForWar() { //-  System.out.println(this.getClass().getSimpleName()+" reporting.."); } } 

 public class Boltons implements House { @Override public void prepareForWar() { //-  System.out.println(this.getClass().getSimpleName()+" prepared for war"); } @Override public void reportForWar() { //-  System.out.println(this.getClass().getSimpleName()+" reporting.."); } } 

Note: this.getClass().getSimpleName() simply returns the class name

Next 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.

Source: https://habr.com/ru/post/343250/


All Articles