Dependency Injection in C# ❘ A Hands-On Guide to Boosting Code Flexibility and Testability

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
dependency injection is a must have in every professional developer's toolkit but for beginners it's very intimidating it has a big fancy name and a lot of times when people are trying to teach it they're doing it later on in the learning experience and I found that they tend to pack too much complexity into their example so what I would like to do today is take some code that's very simple written the way a beginner would write it and we will refactor it to use dependency injection so that we can demonstrate the basic concepts I'm Eric Wise from skill Foundry where we teach people how to code the right way for a sample application we have two files we have a game manager and we have program.cs now the game manager is just a very simplistic class that plays a round of rock paper scissors and what it does is it uses two enums we have a choice rock paper scissors and we have the result of the round which either player one wins player 2 wins or it's a draw and this game manager class has one private field which is a random number generator for the computer to pick a choice and it has a method called play round and the play round method first it prompts player one which is a human for their choice and it converts RPS to a choice of rock paper or scissors and if they don't enter RPS it's going to prompt them to enter it again now player 2 the computer it's going to generate a random number it's going to convert that to a choice print out what they picked and then it's going to determine whether the result was a draw or if Player 1 wins or if neither of those two things happen then player 2 wins over in program desk yes the only thing we're doing is creating our game manager object and then we put in a do while loop so that it will continue playing rounds so long as the player says that they want to play again and it takes that round result from the play around method and it just parses it out in an if statement and prints out the result and if I run this you'll be able to see very quickly that this application does work so I can enter Rock player 2 also picked rock it was a draw I can enter scissors they picked Rock so they won and so forth so the application is clearly working the application is really simplistic but this application has some pretty serious problems the main one being how do you test it and the answer is you can't you cannot write automated code tests for the play around method for starters it goes to the console it requires human input to run and that can't easily be automated and number two because the computer gets its data from a random number we can't control what the computer picks and if we can't control what the computer picks that means we can't write tests for it because we won't know so the only way to really test this application right now is to brute force it and just run it over and over and over again and hope that the random numbers line up in a way that you can test every possible combination that is not efficient that is not how professionals like to write code now as a beginner who does not know dependency injection you might start thinking of some ways to solve this problem and usually what it involves is hey we could add some parameters to the playground method we could pass in maybe a Boolean that would put it in test mode and then we could add a bunch of if else statements and and kind of put that test code inside the game manager class and that's bad because now anytime in the future you want to change the behavior of how a choice is received you now have to edit the game manager class and what happens if you accidentally break one of the other choices how big and complicated is that class going to get as you add more and more if else statements because if you think about it you need every permutation of choice you need rocks papers scissors all of those and this is where the concept of dependency injection comes in the question we need to ask is does the game manager really need to be concerned about how player one and player 2 arrive at their choices and the answer is no it doesn't really need to care what the game manager cares about is it says Hey player one give me your choice hey player two give me your choice it doesn't care if it's random it doesn't care if the choice comes from the console it doesn't care if the choice is hard coded this is where dependency injection shines and what it tries to accomplish we want to decouple the game manager of class from the implementation of how a choice is made so now that we see the problem with the game manager class let's talk about the term dependency injection this game manager play around method it depends on two choices being made you cannot play around of rock paper scissors without getting those two choices but what we don't want it to do is be concerned with these details it should not be managing the process of how player one determines its choice and it shouldn't be concerned with how player 2 gets its Choice it should just ask for them so what we're going to do is we're going to pull this code out of the game manager class it's still going to exist but we're going to put it in different objects and those become like swappable components so dependency injection it depends on a choice and because we've moved the code somewhere else we're going to inject the code at runtime into the game manager we're going to say hey game manager here are the two objects that are going to give you your choices that you need and the game manager is going to say great object one give me your choice and that code will execute and it will pass the choice up and then it will say hey object two give me your choice and that object's code will execute and it will give it the choice the game manager will not see or care where that choice came from anymore it depends on it but the objects are going to be injected so when you think about dependency injection as a term all we're talking about is being able to swap in objects that have different implementations which is how they do their jobs now as we jump over to code the first step of dependency injection is taking that behavior we want to swap and expressing it as a method because that's what an interface really is it is a list of methods and behaviors that every object that implements that interface must have now for rock paper scissors here this is really simple it's only one method it needs to get a choice from a player so let's go ahead and name the interface iPlayer and Define one method stub on it called get choice so we'll go ahead and do that we'll add a new item it'll be an interface we'll say iPlayer and it will have one method stub on it and it will be get choice just like that and now the interface has been defined this is the only thing the game manager needs to do its job it needs a choice doesn't care where it comes from we care so let's go ahead and put in two implementations so let's add a class and we're going to call this the human player and the human player is going to implement the iPlayer interface and when we do that we have to put all the members from the interface into the class and we defined get Choice which returns a choice now what we can do is go over to our game manager and go ahead and just copy all of this implementation code and we can put it in the human player and at the end of it will return the choice just like that and now all of that logic has been moved out of the game manager it's now in this human player object ready to go and the second thing we're going to do is create a class for the computer so let's say computer player and same thing we will implement the interface iPlayer which will require us to put the choice method on there and this class will also need the random number generator just like we did before and now back in the game manager it doesn't need this anymore it can take out the implementation from the computer and we can move that into this method and we can return it just like that so now all of our implementation has been removed from the game manager the computer player when it's asked to get a choice we'll use a random number the human player when it's asked to get a choice will prompt the human player using the console so the behavior of the game hasn't changed it's just moved into two different components now going back to the game manager this class is no longer happy because it has variables for choices but it has no way of getting them because we've removed the implementation and moved it out to the human player and computer player objects so the next step is to create fields that hold iPlayer objects that the game manager can ask to get its choices now independency injection there's a couple different ways you can pass objects in but the most common is to use a Constructor method and this is called Constructor injection all we're going to do is say hey when you create a new game manager object you must also pass it the objects that will get the choices for it the objects it depends on so what we're going to do is in the game manager we're going to create two fields of type iPlayer and we're just going to call this player one and we're going to call the other one player two and then we're going to add a Constructor method where we're going to pass in two iPlayer objects now we're coding to interfaces not to the implementation we will never mention human player or computer player in this class because this class doesn't care all it needs is an iPlayer doesn't care what kind and it's going to ask it to get a choice so you're always going to only use the interface in the injected class so we're going to say iPlayer player one iPlayer player two and then in the Constructor method we're going to populate our local Fields so that that data is saved and the object like that and now in our play round code all we're going to do is say Choice P1 equals and we're going to ask player one with the interface to get its choice and then we're going to do the same thing for P2 we're going to ask it to get its choice and now we're done with this class you're going to give it two implementations of iPlayer it doesn't care if they're human computer or any other implementation we might have the only thing it's going to do is receive those components and then it's going to ask those components to get the choice no longer cares where the choice comes from does it matter because it's going to run the game regardless and let's go ahead and put a message in here just so we know what people picked so I'll say console.writeline and we'll say player one picks that and then we'll say two picks that and now we'll know what they picked and the last step here is to go out to our program and when we create our game manager we do need to give it the components now so I'm going to say new human player and new computer player just like that and if we run the game now it should work the same way it did before and you can see it's prompting the human player choose that scissors paper player one wins do I want to play again yes I like rock scissors I want again I'm lucky today but you can see now that we've refactored the code and not broken the application but the game manager has no idea where the choices are coming from when we ran the program we told it use the human player and use the computer player and now this is where the unit testing part comes in so to speed things up I've taken the liberty of adding a test project to our solution and we're going to keep our test code cleanly separated from our game code and this is one of the other benefits of dependency injection is that you can keep this stuff separate it doesn't pollute your production code now what I need to do to write tests is to be able to force a player to pick the choice that I want it to pick and I need to do that without going to the console because I want it to be automated and it can't have random numbers but now that I have the iPlayer interface I can just write an implementation that returns whatever value I tell it to so let's go ahead and do that I'm going to add a class to my test project and I'm going to call this the forced player because I'm going to force it to do what I tell it to do so we'll say Force player is an iPlayer we'll implement the interface and just because something implements an interface doesn't mean you can't add extra stuff the only thing it means is that the extra stuff the game manager won't be able to see it but that doesn't matter because I'm going to set all this stuff up before it gets to the game manager so what I want to do is create a private field that represents the choice that I want it to have so we'll do this and then I'm going to add a Constructor and I'm going to pass in the choice that I want it to use this and then I'm going to see internal choice is equal to the choice that I pass into the Constructor and then when I ask it for the choice I'm just going to return the thing that I set up and now I can do this to always pick Rock always pick paper always pick scissors and I can use that to force wins losses and draws as we'll see let's jump back over to the test class and create our first test method and we'll say on the void rock beats scissors that and I'll say create a game manager now because of the dependency injection I need to give it to player objects well I'm going to give it two Force player objects because I want to force them to get the values of rock and scissors I'm going to say new Force player and you will pick Rock the second one will be a new Force player and you will pick scissors and I hope you can see at this point that I can now force it to pick anything I want which means I can write all of the permutations of the tests very easily so I'm going to say game manager play round and I'm going to assert r equal we're going to say round result Dot player one win of the playground just like that and then I can go into my test Explorer and I can run all my tests and we'll see here that it will run that code and if it goes green we're happy and there it goes it went green so I forced it to pick Rock I force player 2 to pick scissors and then I tested that player one wins when the game manager plays the round so you can see that I could set up multiple tests now and just change these values in each of those tests to all of the permutations that I want and it's very easy for me to do and that's not all that this dependency injection does because now I can swap in any object that implements iPlayer so what if your boss came to you and you this was a game you were running on the job and your boss said hey we would like a computer player but we wanted to pick Rock 75 percent of the time well that's easy you just add a new class you implement iPlayer and you write the get Choice method to pick Rock 75 percent of the time so it's super cool like that in fact I can go back to the program and I can change this to computer player and I can make the computer play against itself and I don't even need to make any changes to the game manager anymore because the game manager does not care where it gets his choices and this is the true power of dependency injection we're swapping Behavior by moving the behavior out to components and then telling the class that uses the behavior here use this component no use this component or maybe you should use this component but I can add an infinite number of implementations of this interface now and I never have to rewrite my game manager class and it also means that I can keep things separated like my test code from my production code and this is why dependency injection is really important to professional developers and it's a concept you need to learn and master now I agree it's confusing at first and it takes a lot of practice because you have to get practice identifying these behaviors and moving them out into interfaces but the concept of dependency injection is not overly difficult all it means is moving implementation code to components and then rewriting the class that uses those components to be very generic such that we can swap them out however we want and it's always just making methods for behaviors and then writing the behaviors so I hope this helped you out with your knowledge of dependency injection I hope this made you a little more comfortable it's still going to need practice but I'm confident that with enough time you're going to get it happy coding [Music]
Info
Channel: Skill Foundry
Views: 20,122
Rating: undefined out of 5
Keywords: dependency injection, dependency injection in c#, c# dependency injection tutorial, c# dependency injection example, dependency injection c#, c# dependency injection, dependency injection tutorial, dependency injection for beginners, core dependency injection, dependency injection c++, what is dependency injection, what is dependency injection c#, Mastering Dependency Injection, in C#, A Hands-On Guide, Boosting Code Flexibility, Testability, Dependency Injection Demonstrated, C#
Id: cCSrPZroICg
Channel Id: undefined
Length: 23min 11sec (1391 seconds)
Published: Thu Aug 24 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.