Hello.
In this lecture, we're going to talk about Question 39, what is the dependency injection design pattern?
Dependency injection means providing the object that some cross needs.
So its dependencies from the outside instead of having it construct them itself.
Let's see this in practice.
First, let's take a look at the cross that does not use the dependency injection.
The personal data formatter needs to use longer and people data trigger it means they are its dependencies
in this code.
The Personal Data Formatter creates the logger and the people data reader object itself using the new
operator.
They are a couple of issues with this design.
First of all, personal data formatter depends on a very particular implementation of people's data
reading logic.
What if we wanted to use a different data source?
We would not have any way of doing this, as this class commits to using the specific people data reader
object by creating it with the new operator.
Now, those two classes are tightly coupled, and this is particularly problematic when we want to.
You need to test this code.
Let's assume that the people data trigger connects to a real database and sources people information
from there.
If we created the personal data formatter in this fixture, it would instantiate the people data reader,
which would try to access the database.
This is not acceptable in unit tests.
We must have a way of providing a mock implementation instead of the current design.
It's not possible.
We'll learn more about MOCS later in the course.
Lastly, we are breaking the single responsibility principle here.
The personal data formatter should only be responsible for formatting personal data, but now it is
also responsible for creating the logo and the people they carry their objects.
In this simple code, this may not seem like an issue.
Let's keep in mind that in real life applications is often much more complicated to create an object,
as it may have many dependencies of its own.
Let's refactor this code to use dependency injection.
First of all, let's make the people data reader implement an interface.
And now let's inject this dependency to the personal data for Masterclass instead of creating it right
in a.
All right.
As you can see now, the personal data formatter is given the objects implementing the interfaces it
needs instead of creating them itself.
This solves all problems mentioned before the glasses are now loosely coupled.
We can easily switch the objects.
We passed to the personal data for constructor, to any other objects implementing a logger and old
people data reader interfaces.
If we need, we can also do it at runtime.
Personal Data Formatter doesn't know anything about the concrete people data we get across.
All it cares about is that it's being provided a dependency that can retrieve people's data.
It doesn't care how it's done exactly when we would be creating unit tests for this class.
We could easily provide a mock of the old people data reader interface to avoid connecting to our real
database.
The Personal Data Formatter is no longer responsible for creating the logger and the people data reader
objects.
The creation of those objects and using them are separated.
The single responsibility principle is not broken, and we maintain the separation of concerns.
As you can see, the dependency injection is a straightforward pattern.
It it solves a lot of problems.
In C-sharp, we most typically use the constructor injection, so the dependency is injected to a class
via its constructor.
It is also possible to inject a dependency via a setter, but it is much less popular US in general
having a public sector.
These are risky business, but if we wanted it, it would look like this.
And then we'll simply assign this property toward the object implementing ogre interface.
We wanted to use here before we mentioned that classes should not be responsible for creating their
dependencies.
Well, who should be responsible for it then?
Most typically, we have two places where we construct objects depending on our needs.
If we need objects that can be constructed right of the program start, we can create them at the entry
point of the application, like the main method and then pass them down to whatever cross that needs
them.
In this case, we create or object we need in the entry point of the application and then we pass them
around to all classes that need them.
Please notice that in the real life projects, the creation of objects is not done manually, but with
dependency injection frameworks, there are mechanisms that automatically create dependencies and inject
them to objects that need them.
Dependency injection frameworks are configurable so we can decide what concrete types will be injected
into objects.
They can also be configured to produce one instance of some type or to create separate instances for
each object that needs them.
Some of the popular dependency injection frameworks in C-sharp are out of our own inject.
The second place where we create objects are factories.
Sometimes we don't want to create objects to the entry point of the application, but rather at some
specific point of execution.
This happens when we are not sure what objects exactly we need.
For example, a concrete type may depend on some parameter or configuration provided at runtime.
Sometimes we're not even sure if we will need those objects at all.
In this case, it's best to use a factory.
A factory is a class that simply creates objects of some type.
Let's see an example.
Let's say that in the personal data for motocross, we can either use the default formatting which we
used here or formatting provided from the outside.
The decision on which one will be used is done at runtime, and it depends on the value of a parameter
of the form.
What mattered?
If the default formatting parameter is set to true, we'll just use this default formatting, but otherwise
we want to create a format or object.
There is no point in injecting this object into this class because it may happen that we won't even
need it, so it would be a waste of resources to create it.
But I don't want to create it here like this.
Because this a gun would be against the dependency injection and would cause again all the problems
we had before, when we created Roeger and people they carry, they're here.
So I want to create this object right here, but they want the benefits of dependency injection.
The solution is to use the factory first.
Let's see the factory glass.
It is, as you can see, it's extremely simple.
It's just creating an object, implementing some interface.
Let's use it in the people data for through glass.
All right.
Let's see again what happened.
First of all, I injected an object implementing the 04 motor factory interface to this class because
I don't want to go against the dependency injection design pattern.
Then if this parameter is false, I use the factory to create a formatter object.
And finally, I use it to format the people's data.
Please note that the factory would dance on interface.
So for testing purposes, we can provide a mock of the factory that would create a mock of an actual
formatter motor because we use the factory.
We don't need to create the filmmaker object up front.
Maybe it will not be created at all.
If the format method is never called with the default formatting parameters set to false.
Of course, in this simple code, it wouldn't matter that much.
But again, in a real life application, the creation of an object might be more complicated and performance
costly.
All right.
We learned that dependency injection is a design pattern, according to which we should provide the
dependencies that an object needs instead of having it construct them itself.
During the interview, you can be asked what our dependency injection frameworks?
There are mechanisms that automatically create dependencies and inject them into objects that need them.
They are configurable so we can decide what concrete types will be injected into objects, depending
on some obstructions.
Some of the popular dependency injection frameworks in C-sharp are out of luck or indirect.
What are the benefits of using dependency injection dependency injection the caboose across from its
dependencies?
The class doesn't make the decision what concrete type it will use.
It only declares in the constructor what interfaces it will need.
Thanks to that, we can easily switch the dependencies according to our needs, which is particularly
useful when interpreting mock implementations for testing purposes.
All right, so we learned about dependency injection.
Thanks for watching, and I'll see you in the next lecture.