So, dependency injection is yet another design pattern.
We've seen one model view, ViewModel, and
now there's another called dependency injection.
So, it's a design pattern that implements something called inversion of control for
resolving dependencies.
So, inversion of controls is a construct or an approach for resolving dependencies.
To understand what inversion of control means, let's take a look at how we usually
go about architecting a couple of opponents in let's say a shopping cart.
So the regular control will be,
if we have a shopping cart, we can have a separate module called CardProcBank1,
basically a card processing, credit card processing for Bank1 module and
it has one method charge given the credit card number and the amount.
And the reason we have a credit card for a particular bank is because, for
that particular bank, we might have a particular URL that we have to contact
a bank through and that particular can have its API.
So we don't want to take all that code and stick it directly into the shopping cart
because our shopping cart would be very much and very closely tight.
Would have tight coupling with a particular bank API.
We don't want to do that.
So the way we would instantiate the cart processing,
is we would create that cart processing module inside shopping cart.
And then we'll just call charge, and
call charge giving it the number and the amount.
And clearly in this diagram, we see that the shopping cart module depends
on the card processing module for bank #1.
But what happens if tomorrow we decide that we don't like this bank,
and we want to use a different bank because it has better prices for For
credit card processing fees or something like that.
So the way we would do that is, we would have to write a separate card processing
bank 2 that also would have the same type of charge with the number and amount.
And in that case it means our shopping cart code will have to slightly change And
now when we create the instance of the credit card processing module,
we would have to create it with obviously credit card processing for Bank2.
And then as before,
we would call that charge method passing at the number and amount.
And here again you clearly see that the shopping cart
again depends on this card processing for Bank2 module.
But we have a huge problem here, we have to change the code
inside the shopping cart which means we have tight coupling again.
Just because we switched which bank we process our cards through
we need to change the code inside the shopping cart?
And how do you test the ShoppingCart?
Let's say you only have a couple of simple methods, add to card and remove from card,
in this scenario you wouldn't have to instantiate a whole credit
card processing system just to see if your cart, your ShoopingCart Can
place something into the right array and remove something from that array,
keeping counts of total value of the shopping cart, total taxes, and etc.
That would be insane.
We can of course create a fake credit card.
Processing module and instantiate that instead inside a shopping cart.
But that would once again require us to change the code in the shopping cart.
And having to temporarily change the code in the very function
that we're trying to test seems like a really backwards and a bad idea.
Who says we won't mess up something while changing the code for
the test, and then putting it back the way it was?
So what's the solution?
The solution is Inversion of Control.
And in this particular approach, our shopping cart functional module
accepts a credit card processing module Is an argument and we still have that
line where we call the charge method on that card processing module but
if now is using the instance that we were passed from somewhere else.
We get still have those separate modules but in this case, some Overall system
is going to be the one who is going to instantiate that credit card processing.
So in this case would instantiate the credit card processing for bank one.