Dependency Injection Made Simple with Java Examples | Clean Code and Best Practices | Geekific

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
What's up geeks and welcome to the channel! If you're a software developer or   you're studying a programming language you may have heard of: Dependency Injection.  Personally, at first, I thought this must  be very hard and maybe you did as well!  But I assure you it's just a fancy term  used to represent a very easy concept.  You see, in any object-oriented language classes   and objects are the foundations of  any functionality you can think of.  Now, the relationships between these classes  and objects like composition or inheritance  make it possible to extend and  re-use some of these functionalities.  However, there's a catch, the way that we choose  to build those relationships, those dependencies,  determine how decoupled and  reusable our code will be,  not only in terms of our actual  code, but also in terms of testing.  So, suppose we have this Food interface  implemented by the Burger class,  and well these two are  being used by the Chef class   which has a single method called prepareFood. In this simple example we can notice that the   Chef class is dependent on the Burger class as without initializing it in the constructor   the Chef cannot prepare anything. Now, imagine another Chef shows up,   but this one specializes in pizzas. So, we created a Pizza class to account   for this and made it implement the Food interface. In the current implementation we have, to include   Pizzas in the prepareFood method we will either have to get rid of the burger   option permanently and stick with pizzas, or we will have to duplicate the code we have,  get rid of the method we wrote  entirely, and create two methods,  one for each food type we are  trying to prepare in the chef class.  Wait! Actually, some might even say we can have  multiple Chef classes that extend the initial one  where each one specializes  in a particular type of food.  Well, you kind of get where I'm going with  this, all of these solutions are pretty bad,  and the best thing we can do  here is pass a food argument   to this chef object while  it is being instantiated.  So, if we create a chef object  while passing to it a burger object  then we have a chef that  specializes in burgers and so on...  What we did here is passing the dependency  to the constructor of the dependent object   while it is being instantiated, this is called: Dependency Injection.  This dependency was injected into the  object instead of being created inside it.  We decoupled the construction of the Chef class  from the construction of its dependencies,   the Food class in this example. Previously, when we created a Chef class,   a food object was being instantiated automatically  in its constructor because we needed it,  and if a Java class creates an instance  of another class via the new operator  it cannot be used and tested  independently from this class,  it becomes tied to this class  and open for modification,   and this is called a hard dependency. However, when we used dependency injection,   the dependent class was no longer a concern  to us as it is provided from the outside,  this way if the object's  implementation changes in the future,  it is no longer the dependent class responsibility  to figure out what actually changed.  Okay, with that said you need to know that there  are actually three types of dependency injection:  the Constructor Injection,  which is the one we just saw,  the Setter Injection, and the Field  Injection, let's break them down.  In the constructor injection the dependencies  are provided through a class constructor.  In the example you see, we provided the  class we depend on via the constructor   while initializing our main object this type of injection is the most recommended   and we'll see why in a bit. For the setter injection   the client exposes a setter method that  the injector uses to inject the dependency.  So, instead of having the  dependency as a constructor   parameter we will have a setter to pass it. In this example, to create our object we made   use of the default constructor and after that we used our   setter to provide that class its dependency. However, this approach is not really recommended,  because you see by doing  that we hided that dependency  and by reading the constructor  signature or creating our main object  we cannot identify that there is a  dependency right away which might   cause a NullPointerException at runtime. And finally, there is a third way to   inject dependencies in Java and  it is called field injection.  The only way for field injection to  work is either directly mutate the field   because it's a non-private and non-final field, or modify a final/private field using reflection.  This approach has the same problem we  discussed in the setter injection approach,  and additionally it adds complexity due  to the mutation or reflection required.  Okay, now that we know what is dependency  injection and how to inject our dependencies,  let's see why do we even want to do that? Why would you want to apply dependency injection?  Well to answer this question we will have to  take a look at the concept behind dependency   injection called: Inversion of Control. Inversion of control is a principle in   software engineering which transfers the  control of objects or portions of a program   to a container or framework. You see, in contrast with traditional   programming in which our custom code makes  calls to a library where reusable code sits,  inversion of control enables a framework  to take control over the flow of a program   and make calls to our custom code. To enable this, frameworks use abstractions.  So, if we want to add our own behavior  we need to extend the classes of the   framework or plug-in our own classes. This is also reflected in the fifth principle   of SOLID which are the five basic principles  of object oriented programming and design.  This principle states that: A class should  depend on abstractions and not on concretions.  And that is exactly what we did when we injected  the Food class to the Chef class if you recall.  With a hard dependent code,  our Chef class was dependent   on the implementation or the Burger class, however when we used dependency injection   we became dependent on the abstraction or  the interface instead of the concretion.  You see a class should concentrate  on fulfilling its responsibilities   and not on creating objects that are  required to fulfill those responsibilities  and that's where dependency  injection comes into play,  it provides the class with these required objects.  By doing this you'll be reducing  boilerplate and duplicate code  as the initialization of the dependencies  is done by the injector component.  Additionally extending the application  and its functionality becomes easier,  your classes are now way more open for  extension and closed for modification.  Moreover, you will find greater  ease in testing your program  because your dependencies can  now be isolated and mocked  allowing components to  communicate through contracts.  Now, given all of this I would like to point   that dependency injection is a  solution to certain problems,  so before applying it start by asking yourself  if you even have a problem in the first place.  If not then using it will most  likely make the code worse.  You have to consider first if you can reduce or  eliminate dependencies or if mocking will really   facilitate your testing or if it will obstruct it. As an example suppose you have a class   A which uses another class B. Internally B is only used by A   and therefore fully encapsulated and can  be considered an implementation detail,  if you change this to inject  B into the constructor of A,  then you have exposed this implementation  detail and B has to exist in some other   place in the system separately from A leading to an overall worse architecture   with leaking concerns. Another problem you   may encounter is while testing. Suppose you have a class B injected into the   constructor of A and while testing A you mocked B. Now, even though your tests are passing,   you will never be fully sure that the actual production code   of A will work with the actual production  code of B because all you used was a mock.  So, my last words on this topic  are that dependency injection   should be used whenever you deem it necessary as sometimes you might find yourself adding   complexity to your code instead of simplifying it. And that's it for this video, I hope it was   helpful thank you guys for watching take  care, and I will see you in the next one!
Info
Channel: Geekific
Views: 41,450
Rating: undefined out of 5
Keywords: geekific, programming, coding, computer science, dependency injection, java dependency injection, object oriented programming, design patterns, dependency inversion, dependency injection java, object oriented programming java, programming with anthony, programming for beginners, computer science major, coding interview questions, coding basics, inversion of control, IoC explained, inversion of control java, dependency theory, dependency inject, dependency injection explained
Id: GATSXm7WAxU
Channel Id: undefined
Length: 8min 44sec (524 seconds)
Published: Sat Mar 26 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.