Intro to Dependency Injection
Hady ZainEldeen
Senior Android developer at Areeb Technology | Android | Kotlin | Jetpack Compose | KMP | CMP | Clean architecture
Dependency or dependent means relying on something for support.
Classes often require references to other classes , for example car needs object from wheel class so car is dependent on wheel
as shown below
class Car { private Wheel wheel= new Wheel(); public void wheel() { wheel.go(); } } class MyApp { public static void main(String[] args) { Car car = new Car(); car.go(); } }
so there are problems using this hard dependency
Car and wheel are tightly coupled - an instance of Car uses one type of wheel, and no subclasses or alternative implementations can easily be used. If the Car were to construct its own wheel , you would have to create two types of Car instead of just reusing the same Car for wheels of type MRFWheels and Yokohama.
The hard dependency on wheel makes testing more difficult. Car uses a real instance of wheel, thus preventing you from using a test double to modify Wheel for different test cases.
how Dependency Injection help us to solve that ?
Instead of make Car class construct it’s own wheel there many types of DI can solve this problem
- constructor injection: the dependencies are provided through a class constructor.
class Car { private final Wheel wheel; public Car(Wheel wheel) { this.wheel= wheel; } public void go() { wheel.go(); } } class MyApp { public static void main(String[] args) { Wheel wheel= new Wheel(); Car car = new Car(wheel); car.start(); } }
2. setter injection: the client exposes a setter method that the injector uses to inject the dependency.
3. interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.
So now its the dependency injection’s responsibility to:
- Create the objects
- Know which classes require those objects
- And provide them all those objects
If there is any change in objects, then DI looks into it and it should not concern the class using those objects. This way if the objects change in the future, then its DI’s responsibility to provide the appropriate objects to the class.
the concept behind DI
DIP ( Dependency inversion principle) — It is the fifth principle of S.O.L.I.D — which states that a class should depend on abstraction and not upon concretions (in simple terms, hard-coded).
According to the principles, a class should concentrate on fulfilling its responsibilities and not on creating objects that it requires to fulfill those responsibilities. And that’s where dependency injection comes into play: it provides the class with the required objects.
Benefits of using DI
1.Helps in Unit testing.
Decoupling is essential for unit testing. DI is a great way to achieve decoupling.
2. Boiler plate code is reduced, as initializing of dependencies is done by the injector component.
3. Extending the application becomes easier.
4. Helps to enable loose coupling, which is important in application programming.
You can implement dependency injection on your own (Pure Vanilla) or use third-party libraries or frameworks.
why we need those libraries as we can inject instance by hand or manual ?
- For big apps, taking all the dependencies and connecting them correctly can require a large amount of boilerplate code. In a multi-layered architecture, in order to create an object for a top layer, you have to provide all the dependencies of the layers below it. As a concrete example, to build a real car you might need an wheel, a transmission, a chassis, and other parts; and an wheel in turn needs Hub and Rim plugs.
- When you’re not able to construct dependencies before passing them in .
so when your injection work increase you better use third-party library the most famous library in android is Dagger
Dagger facilitates using DI in your app by creating and managing the graph of dependencies for you. It provides fully static and compile-time
Choosing the right technique for your app
Manual dependency injection only makes sense for a relatively small app because it scales poorly. When the project becomes larger, passing objects requires a lot of boilerplate code.
Dagger is built to scale. It is well suited for building complex apps.
Libraries and Frameworks that implement DI
- Spring (Java)
- Google Guice (Java)
- Dagger,Koin (Java and Android)
- Castle Windsor (.NET)
- Unity(.NET)