Design Patterns: Dependency Inversion Principle Explained Practically in C# (The D in SOLID)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
when you're writing code are you doing it right that's the question that worries a lot of people and should probably be at least something that every developer thinks through design patterns are best practice concepts that we can implement into our code to make it better in some way thing has guardrails to keep our code safe in today's video we're going to looking at the fifth and final entry in the famous solid principle the d stands for the dependency inversion principle we're gonna dive into what that means how it should change our programming practices and how far we should take it for those of you who don't know me my name is Tim quarry and it's my goal to clear the confusion and frustration around learning c-sharp because learning software development shouldn't be so hard if that sounded something that would interest you I think you'd benefit from two things first of all subscribe to this channel and second join my mailing list the million lessons we're gonna get exclusive content and insider access you'll find the link in the description below also in that same description you'll find a link to my blog post on this topic where I'll include links to the source code at the beginning this video and also at the end so let's start a conversation on dependency inversion principle which I'm not called di from now on by looking at some code I could do a nifty PowerPoint for you and show you the fun pictures to try to represent this issue but I found best to look at this in a practical manner here I've created a project in Visual Studio in c-sharp now the principles as video actually apply to any software development language but since this channel focuses primarily on c-sharp that's the language we use the code here I have is rather simple it's a console application that simulates a really simple chore application in fact I've oversimplify it in order to hopefully make it easier to understand di we start off with our static void main which is the entry point for a console application where I create a new person object and new chore object I assign the person object which I call owner to the chore object as the owner so the chore is simply has the take-out-the-trash property or turning and it has the owner and I do three things I perform work and say three hours of work was done I perform work against a 1.5 hours of work was done and finally I say that mature is now complete now if we look at the actual code of this in the chore class we see the chore name and owner properties and we have the hoursworked property as well which is actually a private set because we actually set that through the perform work method and that way we can do more than just perform work we can also do other things now I could have made that into a full property and put this stuff in a set but I actually wanted it into a method so we add to the hours worked we write a new log or create a new logger instance and we log out that we perform the work on and the chore name when we complete again it marks the property is complete done and it creates a new log instance that says we completed this chore and then it creates a new email instance which sends an email to the owner saying the chore is complete so let's look at those two classes let's look first through the logger it just does a console.writeline that says hey i'm i'm logging this it actually so does the send email these are just simulations are not actually a full application I want to try and keep it as simple as possible okay so and finally have a person class which is just a really simple first name last name phone number an email address so that's it that's our entire application if we run this I'll drag it over here it says we perform work which that's our first one we perform work again that's a second one and then we completed taking out the trash and we're simulating sending an email to Tim I am Tim quarry calm all right so that's the entire application that's all it does now the propolis application is it totally violates di so now we have some code and from us let's talk about di what is di states that high-level modules should not depend on low-level modules but rather both should depend on abstractions and those abstractions should not depend on details now that's a whole lot of fun words that kind of heat glaze over a bit so let's talk that through and what it means so in this instance a high-level module means anything that's calling something else okay so for example static void main it calls into the person class it also calls into the chore class okay so it depends on the person class being there and the chore class this would be a high-level module the static void main because it depends on something at a lower level now that your class is actually a high level module as well because it depends on the logger and the email or class the email I depends on nothing and you notice a logger be to what we call a low-level module so the high level modules chore and program depend on the lower level ones meaning I couldn't just change out how the say logger works because that would break chore which and break our program does so yes if we depend specifically on that specific class so that's the dependency that we have these high level modules depend on the low level modules and that's what di is saying we have to invert okay we have to have have a Changes story so that the low leveled modules depend on the higher-level modules and in fact it's saying that both should depend on abstractions now there's a couple ways of doing abstractions but the simplest way and the only been doing a lot throughout this series on solid is with an interface and an interface works doubly well in this case because the fact the second part of the description of what di is it says that both the high and low level modules should depend on abstractions and those abstractions should not depend on details meaning those abstractions shouldn't have to know about how things get done they should just say these are the things that will get done that's a really clear interface and so interfaces are a clear way of doing di well now let's start our transition to a DI type system by first working on this person class it's really easy one it's a simple one if I just right-click on the person and do quick actions and refactorings and say that I want to extract an interface called AI person and I want all four properties that's great there you go really simple I now have a interface called AI person so now what we could do in theory you might think we come back over here instead of saying person here we could say AI person and now it's a panel interface right yes yes it is now I did break chore so let's fix that real quick before you go on where we say it's actually an AI person so and email sender see how and I have a change everything now because everything is is tied to the person class and that's a dependency we want to kind of break we want to invert that now I have made this now and I think that should be about it if I build that that should probably build and did with no errors so I've now gotten interphase the person but here's the deal I'm still depending on the person class I haven't really inverted the dependencies because this high-level module still depends on the very specific person class even though I'm assigning it to an interface instance or an interface variable and that's a problem because I'm still having to have that dependency at this high level module which is what di is saying I shouldn't have in fact whenever you see yourself doing new so new person nooch or a new logger new email er all these are tightly coupled dependencies on lower-level modules that's we're trying to break we're trying to break those dependencies on those lower-level modules and instead work off interfaces so what we do is have some way of not having to say new person now this is going to this is going to start covering territory that gets into a different di and so we're calling de pen see in version di but I'll probably lot of you have heard about dependency injection in fact a logical get confused on the D in solid thinking is dependency injection and it's not it's a different thing dependency inversion is the principle dependency injection is one of the ways you make the principle work it's not the only way there's other ways as well but it's probably the most common way and it's the way that I would encourage you go but for the sake of this video for a sake of clarity I am NOT gonna cover to pan C injection in this video I'm gonna do it in a one coming up very shortly but I want to cover just dependency inversion because I don't want those two things to be associated too tightly together in your mind there's other ways of going about this depending on your situation with that being said this is where it's going to get a little trickier or a little muddy if we're not careful so they'd be very clear here at some point you have to new up an instance so it's going to be that somewhere in your application but with here and here and all over the place that's a problem so what dependency inversion expects is that you will centralize that into one place that you can easily swap things out and so what we're going to do for simplicity sake is we're going to create one class in console UI which is the opposite of I normally do I don't usually put classes here but I'm going to for this demo I'm gonna put one class here I'm gonna call it my let's call Factory like a public static class and what its job is going to be is to new up instances when I need them see one place that has all the dependencies so I will make a public static person create person yeah bring in the using thing for that and I will just return a new person now sometimes a fact early hassle more complicated Matt but this is a very very simple place in a very very simple example we're going to do here public static chore public static well and here's where we actually need to change this to an I person and will have to have an ID chore as well so let's it's actually wait on that I'll comment it out for now let's go to the chore and create our interface for it as well looks good does everything we need to do I'll save that and return a ID Shore now I can do that right away because when I create that interface and also assign that interface to the chore class which is a really handy little little step there now before I create one for email or what's let's create it but actually we're going to rename pretty much everything so let's create it instead of ie mailer I I message sender I'll rename that and let's say send message okay and now if we come back over to email our we have email or is an I message sender we have send message I use that that rename in the interface to rename this as well and the reason why I did that is because maybe I want to send a different message via text message or a message through the database or something like that well I can do that now because it's more generic as a message sender now longer is pretty much a logger I mean you can have different things that doesn't logger but still do the same basic action of logging therefore this interface will work great so I'll be an ilogger they'll have a log good to go go back to the factory and we'll create those last two whoops I'll logger create new logger and a public static I'm message sender returns new for it now and new email er okay so what I done here well I've create a static class that news up but if for different types of class instances that I need to have and I have accessed this now at the very top level at programmed ICS so now when I need something like a new person let's go ahead and comment this out for right now so I'll just comment it out instead of doing yes new person I can say factory dot create person and now I can just do owner dot first and all the rest so let's uncomment this again and I will do owner dot first-name and yes it's going to be only quit a bit until I get this all the commas train are transitioned over into semicolons okay so there we go so now I have called this factory dot create person that's the same thing with this new chore so instead of chore it's going to be an H or and we're going to say instead of new chore we're going to say factory dot create chore and again we ready to the same exact thing here we're going to do chore dot short name and chore dot owner and make the semicolons okay so nothing else at this level has changed okay so we've just gone interfaces instead of the actual implementations and to get our implementation we have called the factor to get for us so now let's go through the rest of our application and extract out the places where we have a or we have a new and change that over okay so let's start with our our logger has nothing it just logs that's great our emailer has nothing it just logs or writes the console so that's great and our person as well has nothing so we just have to go look at the chore class now a short class has a owner which is the I person that's fine but we have as new logger now we could do is have this call the factory but that would then create a dependency that I don't want I want that high level to just give me what I need so what I'm going to do is actually create a constructor for my chore class so ctor I'm gonna say give me an eye logger let's call this log or logger loggers fine and also I want a emailer which is an eye message sender let's call it message sender anthalie we pass these in as a constructor then I can do is create private variables so I'll create a a logger underscore logger why the underscore and an iMessage sender on ask or message sender and then in our constructor we can say underscore logger equals logger and underscore message sender equals message sender so now we have two interfaces passed in at our constructor level what does that do for us well instead of mooing up a logger we just instead of saying logger or create log I should delete that and we say underscore blogger dot log we the same thing here so underscore log or log and for here we say underscore message whoops underscore not + honor score message sender dot send message so what have we done well we passed in two interfaces into our chore class we use those two interfaces to then make calls to both send a message and also to log a message so there are no more newing up of class instances except in that one factory class now it's yelling us here because it's saying hey I don't have the parameters I need which is not a problem because we actually have access to the things we need in this class in this Factory we can pass in a first we need a logger so create logger and we also need a message sender so create message sender ok so we're going to pass a new logger instance and new email email or instance into our chore class so what happens now well now there are no more dependencies except at the very top level I have to actually ask for that factory dot create person everything else is taken care of for us by the factory so now if I run this as you can see nothing has changed ok so I didn't break anything everything still works the same way except let's look at our application where do we have a new instance or a newing up something nowhere except factory right why is this new for us well it disconnects us from everything so that if we came along and say you know what I want to create a new class instead of emailing It's Made texting so I'm going to create a new class so we'll add that new class we'll call this the texting class not testing texting class well it's called text or since we have email a texter public class texture which implements the eye message sender will implement that instead exception we're going to say let's go a dollar sign a couple of double quotes and in here we will say I am texting person dot first name to say message okay so now we're going to know that it's texting this first person to say this instead of emailing so instead of sending that seemingly guys sending an email to this person we're going texting one more note it still says ie mailer in the clap and the actual file name the even those iMessage sender let's change that little details are important okay so now it's it's very clear what that is okay so we haven't changed I think yet and in fact let's run us again it still says simulating sending an email to Tim I am Tim Cory but what if I come to this Factory and change this from emailer to texture and I run it again I am texting Tim to say the chore take-out-the-trash is complete you see how my application changed it changed immediately with nothing broken I have to worry about breaking anything because I have no direct dependencies I depend only on the abstractions not the commentation so let's talk with those benefits of doing it this way and the first one I just pointed out is the fact that we can make a change to our application in one piece where we replace say emailing with texting and nothing breaks we swap out that part and everything still works the way it used to because we did not depend directly on that instantiation remember I changed the person to an i person and I broke the chore in sense I had to go to chores and fix that and all the rest that is broken now I don't have to do that broken in a good way I don't have to worry about that because if I swap out this create person into the person class maybe they have an employee class instead no problem as long as it still has the same contract we're good to go so that's benefit number one benefit number two kind of links into that and that is now my application is not this one big monolithic application it's a bunch of really little parts and this is where solid really takes us as a whole is it takes us into having lots of really tiny little parts things that do only one thing things that don't have dependencies things that are just self-contained tiny and against an interface so that I can unplug and plug in something new without problems so for example if I look at the longer class and I say okay I need make a change here to do something different no problem I'm making one little change to one little piece of the application I don't have to worry about the implications the rest of my application as long as I still fulfill its contract and of course as long as they don't change the logic in a way that the application was expecting or rather wasn't expected okay so it does allow me to really just work on this piece rather than this project or this solution just this little piece and it kind of breaks it down so that I can make small changes and tweak my application without having big issues to concern myself with I imagine if I had to change its application this is a small one but imagine if I had to change this application before when I started to say nope it's an employee class now I have to changed multiple places just to make that work and then to make sure that I'm breaking anything in those multiple places now I don't have to the another thing it does this is a third benefit third major benefit to allowing this or doing this and that is I'm now set up for dependency injection remember I said essence dependency inversion but the dependency injection is the implementation of this principle or one of the implementations as principal and so what depends the injection does is it takes away the need to do this that create that factor that create person or the actually creation of the factory dependency injection what it does is when it comes across a class like sure it looks and sees oh the constructor has it says it needs two classes no problem I'll go get those classes and it goes and gets them for you so it kind of handles all that stuff for you and along with di or dependency injection actually also comes the ability to have ioc or inversion of control which again is more of a talking about how this principle applies but also a lot of ioc containers will allow you to not only have depends the injection which is where it finds these items here and replaces them with the actual instances but also the fact that it can dynamically change those ends scissor get different ones to panic either circumstances or once you provide it for example I use a tool called MEF in the past that's the managed extensibility framework and it's what Visual Studio was based on and if you notice Visual Studio has this ability to add things to menus and it kind of plug in extra stuff right individuals to do well how does that work well what they do is we have a tool called math where it actually has again depants the injection but it goes and looks for certain dll's and says do you have more things I could add to my di pool so it's kind of having this right here but you a tool goes out and actually looks for DLL with more of these and says oh you have three more loggers I can add and so it brings those three as well so there's some fun stuff you can do and that's kind of a guess benefit number four is that once you have this dependency inversion you also have the ability to have de penser injection but also the ability to then inject dependencies from other sources in a plug-in type framework or a really dynamic application style now that's way down the road that's way on the complicated or endgame type levels but even if you didn't do any of that even if you didn't even do depends the injection and you say you know what I'm just gonna do this factory thing that's all I'm going to do your create my factory cool if you do this then you have an application that's disconnected from each other where nothing depends directly on either the higher level or the lower level classes and instead everything depends on interfaces and that means you can swap out parts as needed to upgrade your application that's huge and this is a the next benefit is not only can you swap these things out but benefit number five is you can unit test this thing that's huge we looked at unit testing all ray a little bit on my channel we're gonna go into it in further detail and soon because once you understand how to do this then you can unit test all this stuff without affecting anything let's firm it think that look remember back at this application before we had this injection of these interfaces before he had any interface we had new everything up if I was going to create a unit test where I create a new chore instance I added the chore name and the owner and then I said I want to test to make sure that the complete chore works every time I ran that test it would have nude up nude up a logger instance which would have written to a log file or written to a logging database whatever it did and also new up a message instance an email a user that's a problem I mean I might run my unit test 50 times in a day well if I did that I don't want my inbox filled up with 50 emails and I don't want my log files filled up with a whole bunch of tests either so that's just not a good idea but because we're so tightly bound to the new email ER and new logger that I couldn't separate this out there for the complete shore method was untestable so was the performed work method for the same reason so therefore I couldn't really test this class hardly at all now since I passing in an ilogger and a I message sender I could create my own class instance that would apply this ilogger insta interface and what would do is just say yep I logged it and yep I emailed it and that could pass those in to my chore instance instead of the real ones now I can test the complete chore and just make sure it called my loggers log method and also my send message my message senders send message okay so I can check those things now I can test those things without having to actually affect real data without writing real log messages without really sending emails may you may think man has something an awful lot of work creating new class instances and they can't really implement the contract and all the rest and yeah that would be if it had to do it manually but I don't it's a thing called mocking and we'll get into that but essentially what it does is allows me to create really quick implementations of any interface that pretend to work so I can test to make sure that my real code which is this this chore class these two methods here we perform work and complete chore that could test to make sure those do their what they're supposed to now you might think yeah but you couldn't test then the logger because a longer probably writes to a log file well guess what instead of actually passing in an actual log file we're going to try and pass that in as an interface as well so there there's a lot of ways we can go down that rabbit hole but essentially when you think about it if you ever see a new somewhere you've got tight coupling we don't want tight coupling we want loose coupling we want talk to just interfaces and so if we do that from top to bottom or from bottom to top either way if we do that through our entire application then we can break up our application into little pieces we can test each piece we can replace it peace and we can use something like a dependency injection framework to actually assign actual instances to each of those interfaces so that's what the dependency inversion principle is all about the idea that we don't have new anywhere in our code that the program DCs does not depend on chore and or person and that chore doesn't depend on the logger or the emailer instead everything is disconnected so that's it that's all dependency inversion principle talks about is just that disconnecting and everything talking to interfaces all right I hope that you've understood this I hope that you can grasp the concept got to tell you what if you can implement especially this but really all the solid principles they're really great and if you implement all them together they kind of form this one larger way of thinking about applications where your applications become more disconnected to become easier to maintain an easier test and easier to work with down the road you're going to thank yourself if you do that because your applications always gonna need to be upgraded think of this way what if your boss comes tomorrow to you and says that big huge application you work on for you know the past six years I know it runs on sequel right now we need to run Oracle if you had properly implanted solid not a big deal and it's not it's not tiny but comparatively yes it is because if you haven't if my solid you got calls to sequel everywhere think of that he said you know what we're going to change out our logging framework from say a log for net to end log and you go well sure but that's gonna take us probably touching every piece of code and testing it and it's gonna take you know six months to do and he says you had a weekend that would be awful but with implementing solid properly you go weekend sure I can have it done the next three hours because I've only change it in one place I'm only changing a few methods and since I'm writing against interfaces no problem I asked matched I create a new class for end log and I write I to a same interface and implement it within log and just say here's how it works instead and I'm done everything still works as long as you test that one small new class secret you're good to go so that's why the power of solid the idea that evidence is connected everything's replaceable everything is modular so you don't have one large car to replace you can just replace a spark plug or you can replace just the hub cap or the wheel all right so that's that's why I'm so so much with a fan of solid and why I wanna make it as clear as possible because once you get it it really makes you code better and that's again coming back to that are you doing it right when writing code and if you are implementing solid you're much closer to doing it right so that's it let me know you think in the comments below if anything was confusing I'd love to hear about it and I will be doing follow-up videos on other principles because there's more than just solid out there for design principles so we're doing more of those and I'll also be covering things like more advanced unit testing with mocking and also depends e injection so they're coming down the road make sure if you're not already subscribed to do so so you can get alerted when those come through all right thanks so much and as always I am Tim quarry
Info
Channel: IAmTimCorey
Views: 156,534
Rating: 4.9653635 out of 5
Keywords: .net, c#, c# training, c# tutorial, code, design patterns, design patterns c#, practical design patterns, programming, s.o.l.i.d, s.o.l.i.d design, s.o.l.i.d principles, s.o.l.i.d principles c#, s.o.l.i.d. design principles, solid, tim corey, training, tutorial, visual studio, dependency inversion principle, dependency inversion principle tutorial, dependency inversion, solid design principles, design patterns c# tutorial, dependency inversion principle c#, uncle bob
Id: NnZZMkwI6KI
Channel Id: undefined
Length: 41min 43sec (2503 seconds)
Published: Mon Apr 23 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.