Dmitry Melnikov
Class A => *uses* => Class BClient => *uses* => ServiceClass A => *uses* => Class B => *uses* => Class CFirst three are active dependencies resolution, the fourth one is a dependency injection (DI).
DI is a providing services to clients from "outside".
Usually DI has at least two meanings:
The act of providing (injecting) the required dependencies using one of the fundamental techniques: constructor, method or field injection (DI technique).
Dependency injection architecture pattern (DIAP).
class Client(private val service1: Service1) { private var service2: Service2? = null fun setService2(service2: Service2) { this.service2 = service2 } lateinit var service3: Service3 fun doSmth() { service1.doSmth1() service2.doSmth2() service3.doSmth3() }}Pros:
Cons:
Pros:
Cons:
Pros:
Cons:
Constructor dependency injection technique looks best by far.
Prefer it to other techniques unless:
Most popular answer at the StackOverflow:
Dependency Injection is passing dependency to other objects or framework
It's incomplete. It's just a technique.
Most popular answer at the StackOverflow:
Dependency Injection is passing dependency to other objects or framework
It's incomplete. It's just a technique.
Should such fields be injected?
val list: List<A> = ArrayList()Most popular answer at the StackOverflow:
Dependency Injection is passing dependency to other objects or framework
It's incomplete. It's just a technique.
Should such fields be injected?
val list: List<A> = ArrayList()
Is it enough to pass a ServiceLocator to a constructor?
Most popular answer at the StackOverflow:
Dependency Injection is passing dependency to other objects or framework
It's incomplete. It's just a technique.
Should such fields be injected?
val list: List<A> = ArrayList()
Is it enough to pass a ServiceLocator to a constructor?
Why DI frameworks exist if DI is just a passing deps into constructors?
Most popular answer at the StackOverflow:
Dependency Injection is passing dependency to other objects or framework
It's incomplete. It's just a technique.
Should such fields be injected?
val list: List<A> = ArrayList()
Is it enough to pass a ServiceLocator to a constructor?
Why DI frameworks exist if DI is just a passing deps into constructors?
If all clients get services from outside, where all of them are instantiated?
Segregates application logic into two sets of classes:
Contains classes that encapsulate core application functionality
Contains classes that resolve dependencies and instantiate objects from a functional set.
Each class in the app should be a part only of one of these sets.
Application +----------------+ +-----------------+|Construction set| | Functional Set || | | || Class1.. | <----> | Class1.. || ClassN | | ClassN |+----------------+ +-----------------+Segregation into these sets is a Separation of concerns.
Concerns of core app's functionality separated from concerns of creating and wiring.
DI — separation of concerns at the highest level of abstraction.
It's not about frameworks, annotations, or any other implementation details.
Different levels of abstraction (class vs application)
Different levels of abstraction (class vs application)
DI techniques - class level Single Responsibility Principle
Different levels of abstraction (class vs application)
DI techniques - class level Single Responsibility Principle
DIAP - application level Separation of Concerns
Different levels of abstraction (class vs application)
DI techniques - class level Single Responsibility Principle
DIAP - application level Separation of Concerns
DIAP implementations use DI techniques under the hood
Different levels of abstraction (class vs application)
DI techniques - class level Single Responsibility Principle
DIAP - application level Separation of Concerns
DIAP implementations use DI techniques under the hood
It's not enough to employ DI techniques to build DIAP
Myths:
Pure Dependency Injection aka Manual Dependency Injection aka Vanilla Dependency Injection aka Poor Man's Dependency Injection
Service Locator ≠ DIAP
git clone git@github.com:melnikovdv/android-arch-2.gitgit checkout 2d28dd5 Application +-----------------------------+ +-----------------+| Construction set | | Functional Set || | | || CompostitionRoot | <----> | Class1 || ActivityCompositionRoot | | ... || PresentationCompositionRoot | | ClassN || MvpViewFactory | | || Injector | | || ... | | |+-----------------------------+ +-----------------+Single entry point class for all dependencies
Dependency graph
Responsible for creating services and wiring them together
Principle of the least knowledge
Minimize class dependencies
Use of Law of Demeter makes fewer dependencies and more readable and maintainable code.
Objects expose behavior.
Data structures expose data.
It's ok to create data structures in-place, but not ok to create objects.
Data structures are Kotlin data classes and Java records.
Non-repetitive definition and exposure of the entire object graph.
You can keep your classes small and focused, but still easily compose them into arbitrary long chains to achieve complex functionality.
It enables Single Responsibility Principle and Reusability.
Testability: easier to write unit tests (test doubles, mocks).
Context isolation (for Android, activity leaks)
BlogItemMvpFragment dependency on BlogItemMvpViewImpl was eliminated in favor to BlogItemMvpView contract.
Sample profit: A/B testing with different ViewImpls. We can change View without changing Activity code.
It's OCP: an ability to modify functionality of a unit without changing its code.
Create Injector class with CompositionRoot to inject fields, not this.somethin manually
Create Injector class with CompositionRoot to inject fields, not this.somethin manually
git clone git@github.com:melnikovdv/android-arch-2.gitgit checkout 33e7216
Hardcoded in Injector which is bad
Performance could be bad either (every time inspecting hundreds of fields inherited from the Fragment is quite expensive)
Pure DI is error-prone and forces you to write lots of code
Can be fixed with a DI framework
External library
Provides a specific template for Construction Set implementation
Provides set of pre-defined conventions
Dagger 2 is the most mature DI framework for now
Dagger 1 by Square, deprecated (reflection based)
Dagger 2 by Google
dagger.android package, deprecated
Dagger Hilt
Dagger is considered the most complex DI framework in Android world
Uses Code generation
Poor docs
Lack of best practices
Too many features
No boilerplate code
Easier to refactor

git clone git@github.com:melnikovdv/android-arch-2.gitgit checkout dagger2Components are interfaces annotated with @Component
Modules are classes annotated with @Module
Methods in modules with @Provides provide services
Provided services can be used as method arguments in other provider methods
Scopes are annotations annoteted with @Scope
Components that provide scoped services must be scoped
All clients get the same instance of a scoped service from the same instancce of a Component
Void methods with single argument defined on components generate injectors for the type of the argument
Client's non-private non-final properties (fields) annotated with @Inject designate injection targets
Component inter-dependencies are specified as part of @Component annotation
Component B that depens on Component A has implicit access to all services exposed by Component A Services from A can be injected by B Serivces from A can be consumed inside modules of B
Subcomponents specified by @Subcomponent annotation
Parent componennt exposes factory method which returns Subcomponent
The argument of the factory method are Subcomponent's modules
Subcomponents get access to all services provided by parent (provided, not just exposed)
Components can use more than one module
Modules of a single Component share the same object graph
Dagger automatically instantiates modules with no-arguments constructors
Dagger can automatically discover services having a public constructor annotated with @Inject annotation
Automatically discovered services can be scoped
@Binds annotation helps to resolve dependency types between interfaces and its implementations
Dagger generates more performant code for static providers in Modules (use companion object or top-level object in Kotlin)
@Component.Builder (or @Subcomponent.Builder) designates inner builder interface for Component
@BindsInstance allows for injection of "bootstrapping dependencies" directly into Component builders
Qualifiers are annotations classes annotated with @Qualifier
Qualifiers are part of the type (@Q1 Service and @Q2 Service are different types)
You can use the standard @Named(String) qualifier
Provider
Providers are basically "extensions" of composition roots
You use Providers when you need to perform "late injection" (factories)
class MyDataService @AssistedInject constructor( dataFetcher: DataFetcher, @Assisted config: Config) {}
@AssistedFactoryinterface MyDataServiceFactory { fun create(config: Config): MyDataService}
class MyApp { @Inject lateinit var serviceFactory: MyDataServiceFactory; fun setupService(config: Config): MyDataService { val service = serviceFactory.create(config) ... return service }}


https://developer.android.com/training/dependency-injection/hilt-android
git clone git@github.com:melnikovdv/android-arch-2.gitgit checkout dagger2
Android component Default bindings-----------------------------------------------------------------SingletonComponent ApplicationActivityRetainedComponent ApplicationViewModelComponent SavedStateHandleActivityComponent Application, ActivityFragmentComponent Application, Activity, FragmentViewComponent Application, Activity, ViewViewWithFragmentComponent Application, Activity, Fragment, ViewServiceComponent Application, ServiceClass A => *uses* => Class BClient => *uses* => ServiceClass A => *uses* => Class B => *uses* => Class CKeyboard shortcuts
| ↑, ←, Pg Up, k | Go to previous slide |
| ↓, →, Pg Dn, Space, j | Go to next slide |
| Home | Go to first slide |
| End | Go to last slide |
| Number + Return | Go to specific slide |
| b / m / f | Toggle blackout / mirrored / fullscreen mode |
| c | Clone slideshow |
| p | Toggle presenter mode |
| t | Restart the presentation timer |
| ?, h | Toggle this help |
| Esc | Back to slideshow |