The Clean Code Talks - Don't Look For Things!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Really great video and good insights into organizing code and DI. Makes me appreciate Dagger because it already does Providers, Factories, Builders for us and helps avoid the 'new' keyword in business logic like the speaker suggests at the end.

Most of the advice is centered on Law of Demeter which is asking only for the things you need and avoid initialization work in the constructor.

Kotlin's simpler constructor syntax helps with this.

class House(private val door: Door)

Avoiding init{} block with code touching requested dependencies seems like a good start for following LoD.

Showing testing snippets and their problems before explaining the issue was refreshing. I hope many dagger articles on the web touched that bit to give a better picture why we need to setup Dagger which really is a bit of work and easily off-putting.

👍︎︎ 10 👤︎︎ u/arunkumar9t2 📅︎︎ Aug 05 2018 🗫︎ replies

An article yesterday about Dependency Injection for beginners kicked off some heated discussion. I stumbled across this talk which does a very good job at explaining why you want to inject dependencies, and how to do it.

👍︎︎ 4 👤︎︎ u/nhaarman 📅︎︎ Aug 05 2018 🗫︎ replies

I remember watching these when they first came out and was impressed.

A few years later when I became an Android dev, I was surprised at the quality of the Android SDK and its programming model, considering the same organisation had produced these videos.

👍︎︎ 4 👤︎︎ u/bart007345 📅︎︎ Aug 05 2018 🗫︎ replies

It's also important to note that while Misko discusses testability in this talk, the biggest advantage of proper DI comes in the form of better maintainability of the code.

Therefore, even if you don't intend to unit test in the near future, you can still claim huge benefits by using DI.

👍︎︎ 4 👤︎︎ u/VasiliyZukanov 📅︎︎ Aug 05 2018 🗫︎ replies
Captions
welcome to another of our clean code talks and today we're going to talk about basically a fancy way of saying dependency injection but I don't want to use the word dependency injection because people kind of have preconceived ideas of what it is so instead let's get to the basics and talk about what exactly why exactly certain things make code really really hard to test in order to test the method the first thing you have to do is instantiate it and that seems like something very very obvious but it turns out that people tend to put all kinds of crazy things in the constructor which makes the instantiation process really really difficult not only do they put things in the constructor people put code into static initialization classes which makes the code again even harder to instantiate because the static initialization class probably goes out and reaches to some kind of singleton expects it to be initialized and so on and so forth so the first thing we have to do in order to test any kind of instance methods is to instantiate the class all right let's get to some fun code and let's talk about this thing suppose you have a piece of code like this and you want to instantiate a document presumably document is used somewhere else like let's say a printer printer wants a document order printed so the document says well I need a URL and it goes on and instantiate an HTML client and says hey HTML client would you be so kind and go fetch this URL and give me the content and now I'm gonna save the content in my local field how would you test this any suggestions and from the audience well you would have to write a test server right you would actually have to instantiate a web server that comes up and you would have to set up the test server with some document that you want and then you have to instantiate the document and then pass the URL that's gonna have localhost to some port and so on you anybody see a problem with this how cumbersome the whole process is so we need to do something about this to make this thing easier to instantiate so any suggestions well what we can do is we can ask for HTML client in the constructor and we can say hey I need the HTML client and in the test I can ask for a mock HTML client and say hey I'm going to override the get method and when you ask for a URL I'm going to return you a document that you we already need now the reason why the previous slide we could have could not have tested and that this slide we have is that in the previous slide we have simply instantiated something that we wanted whereas in the next slide we said we don't care about how this thing gets instantiated always say is we need one of these in order to get our work done ah and so this particular thing is a lot more testable than the previous one because I can always pass in a mock HTML client but even though we can we get rid of a server we still kind of have a problem here and that is every time we need to instantiate a document we have to instantiate in a mock HTTP client we have to prime it with it with an expectation to get a call to a get and a URL and so on and so forth and have a prime to return the value which we want so to make this thing better how about we just get rid of the whole client that get URL and instead we simply say the document wants HTML that makes the instantiation really really easy because I can simply say new document ask for the the HTML code that I want and shove it into my field variable that I need now the reason I know what I need is because I think the stuff that I need is what I save in my fields anything else I ask in the constructor and I don't save in the field it really means that I'm only needing something from there in order to traverse something where else and fetch the stuff that I really want so what you want to end up with is a document like one over here which simply says I need to match the m/l which you can then store in a string now the reason why this is important is because you had a lot of other objects depend on a document suppose you have a printer and if you want to instantiate a printer and test out a print list that you presumably have to pass in a document and would be really nice if I could simply say new document passing the HTML into the documents constructor which I want to print and then test to see if the printer works properly I don't want to go and mock out all the pieces over and over again but let's get back to this doc this document over here for a second what we're doing here is we're mixing the object graph construction with object lookup instead of asking what we need we are constructing it ourselves and I want to demonstrate this through this particular class imagine that just like a document you had a class called the car and just like HTML client you had a class called engine factory and the car wanted to get a hold of the engine in the previous class what we did is we instantiated the engine the diet of the HTTP client which in this case is the engine factory now isn't this a little bizarre that a car knows how to build a factory and then asks the factory to build an engine which then it saves inside of itself I find this rather bizarre but somehow when we are dealing with abstract concepts such as a document HTTP client and so on we easily make this mistake and we don't kind of notice this is a problem only when we deal with concrete objects do we realize just how silly it is for a constructor to be asking for the factory or something that produces something's that are asking for the item directly so again if you just ask for the things that you want the construction becomes really easy because at that point you can simply call a new operator you instantiate the object you pass in all the references that you need and then once you have the document you can easily pass it into the printer or anybody else that needs it now the reason why this is important is because there are many objects the tests are all about instantiating small portions of your application right it's the same drill every single time you instantiate a small portion of the application you poke out the objects a little bit and then you assert some output you instantiate some other piece of portion of the application poke at it and assert some output and if the instantiation portion is difficult you're going to find it really hard to test different pieces and even if it's not that if if the document is difficult it's going to be difficult to read document test it's going to be anything it's going to be hard to test anything which needs the document directly or indirectly so it's a kind of a transitive problem right so one bad apple can really ruin it for everybody else this is basically talking about the test driver the the printer and the document again in more detail which simplifies right you simply instantiate the test driver you instantiate your printer you instantiate your document and you pass the document through the printer in the old setup you would have to instantiate the document the HTTP client which is your friendly which would return the stuff that you need into the document and then pass it into the printer that just makes the whole process a lot more convoluted and difficult to deal with so the first problem in testing is really that the test has to successfully navigate the constructor each time instance is needed and therefore we really really want to make sure that in our tests the new the constructors are really simple preferably ideally we would like to have nothing but assignments inside of the constructor you simply ask for what you need and you save it into your local variable in your local field and you're done with it so now let's look at something called a service locator first of all how many people know what service locator is okay so service locator says basically well there's all these objects which are really hard to instantiate or hard to get ahold of so maybe instead of instantiating them directly or having them as a singleton and we already talked about how Singleton's are hard to test we could maybe go and store the responsibility of getting these objects with a service locator and then we simply pass a service locator around that gives us the scene because we can always override an operation or service locator and then return the object of interest the problem is that the service locator encourages api's which why ah you know because you simply have a class that says I need the service locator and then you look at the api's without looking at the source code and you say gee I wonder what I need to override in order to make that class testable and you have no idea you simply they have to look at the source code or you have to run the code to see where it crashes in your test in order to figure out what needs to be overwritten it also becomes difficult to wire different objects together in the test because while in the constructor you can simply say I need a B and C and then simply instantiate those things with the service locator you now have to instantiate those things but you're not quite sure in which order you have to instantiate them in and then you have to train the service locator to return them in fundament and finally the service locator violates the law of Demeter instead of asking for the things you want you're always asking for the service locator and then reaching through the service locator to get the object of interest so let's have a look what that looks like by the way sometimes the service locator is known as a context and I just want to point out it is much better paradigm than having a global state where you store your Singleton's in so it's much better than global lookup at least your code is testable but it's kind of hard to test and it hides the true dependencies which are as I pointed out it makes the api's lie about what they actually do so let's have a look suppose you have a house in a house and the constructor says I need locator what do we have to override and a locator in order to construct the house no idea you really need to know what is inside of the constructor to figure this out so if you know that you have door window and roof in your fields then you you can kind of figure out yeah I see I need to override the get door get window and get roof so from a testing point of view well before we get the test this is how the API lines right this is what we mean by that it says all I need is a service locator and everything is happy from a testing point of view you have to still instantiate the door root roof and window but then you have to instantiate the locator and in some way you that have to set the parameters on a locator or you have to override the methods or depending on whether it's an interface or not and then it pass the house into it now suppose a house all of a sudden had a new dependency on let's say plumbing well if you had any defense any on plumbing then you would simply declare the new constructor and you would immediately know which tests do not compile because now there's a new parameter in the constructor and you could go into those tests and then fix them whereas if now house needs a plumbing and reaches into the service locator to fetch the plumbing everything still compiles but random tests all over the place are starting to fail and you are not quite sure why it's not exactly obvious that all of a sudden there is a new dependency that was added and you have to deal with them in the step the other way of looking at the service locator is basically that all the objects basically suppose you wanted to reuse the house and give it to somebody else that means you have to give the person not just the house but also all the compile-time dependencies so now house depends on a service locator so now you know you just you can't just give somebody a house to reuse we also have to give them the service locator but the service locator knows about all the other services and objects and cetera whether the house needs them or not the service locator knows about them so now you have a problem that in order to give somebody a house you have to give a service locator in order to give a service locator you have to basically your home application not very reusable so now let's look at it the same exact thing but this time in the constructor we specify things that we actually mean we say we needed Oh door window and a room I don't need to look into the source code or run the code to kind of figure out what I have to miss things ah this is very obvious as a matter of fact I can simply just look at the Javadoc and say oh yeah that's kind of what I mean kind of makes sense that the house needs a door window in a roof yeah I can see that and I'm simply going to go on call the Constructors on these so our test becomes much much nicer right all that stuff regarding service locator the overriding or setting or whatever they just disappeared and now we're just dealing with what we want I personally prefer this kind of API it makes it really easy for me to instantiate objects and work with them because when I instantiate a house and a test I immediately know what else needs to be instantiated not only that I have a choice in my test whether to pass a null for any particular object or the real value suppose I'm testing the painting method of the house presumably I don't need a roof for that so I simply can pass in a null for roof and that basically tells the reader of the test that if something goes wrong you don't look into the roof that's not where the problem is because I didn't even give you one so the thing with service locator is that we are mixing the responsibility of object look up and object creation and from a testing point of view we automatically need either a interface or we need to override it to mock it or we need to have a service locator which basically has a whole bunch of setter methods where we can externally set the dependencies and of course as I pointed out anything which depends on a service locator now depends on everything else whether you like it or not by the way people have lots of different names for this thing so sometimes they call the registry the locator the context the manager the handler the environment the principal and so on and so forth right all different names same exact idea not asking for what you want but really asking for these intermediate object which then knows about the whole world so now let's talk about law of Demeter for a second so imagine you in the store and the item you're purchasing is 25 bucks do you give the clerk a 25 bucks or do you give the clerk your wallet and let him retrieve the $25 yes Paul give them your wallet you let them retrieved 25 dollars why is that in the form of a credit card in a form of a credit card yes oh I think most of us will agree that if you really want to give the cloak $25 right that makes it much easier from a testing point of view as well so let's have a look suppose you have a purchase method and there's a customer and the purchase method says okay give me a customer and I'm gonna reach into the wall and get the money and I'm gonna record some sale or something of that sort right now from a testing point of view we need to now create a customer so we need to create a customer when you create a wallet and we do shove money into the wallet and then we can kind of test this I imagine this thing it's kind of like imagining of a haystack and like you properly position it a needle somewhere in the haystack and then it's it's an understanding between you and the code that you know how to traverse the haystack to get the needle that you want right all this stuff around it is really irrelevant I mean nobody cares about the customer or the wallet they just want the 25 bucks but nevertheless we instantiate this this monstrosity that goes around it in order to get to there are $25 not very interesting test not to mention there's so many objects going on it's kinda hard to figure out if anything goes wrong what what is it went bad now what happens if we change the purchase to simply say hey I need the money presumably $25 right from a testing point of view this greatly simplifies things right I no longer have to instantiate the customer the wallet or anything else now this is kind of a toy example that I'm placing up here but imagine in the real world where the sensation customer constructor doesn't just take a wallet but takes ten other things right so and suppose the customer insist on having doing no checking to make sure you can't pass a noise for all the other parameters and suppose the constructor does real work where it tries to authenticate something from a database right all of a sudden having the previous test makes it really really difficult because you have to pass in a customer and you can't even construct the customer because of all the things we've talked about in previous half an hour so really you really want to make sure that you really just ask for the objects that you directly need to get your work done you certainly don't want to ask for the intermediate object and then reach through that object to get the item that you want and fundamentally that's what's wrong with the service locator is that you're always asking for something that you don't want or you don't need directly only to retrieve the things that you want out of it so the law of the matter basically says you should always ask for the objects you directly need the ones you're going to operate on the ones you're going to dispatch methods on right the dead giveaway is if you have a dot get dot whatever right I mean typically we have a service locator dot get customer repository dot get customer buy ID pass in 37 good luck testing that and so the pendency injection is basically what saves you from all this because it allows you to have all the methods simply ask for the object that you want so here's a little myth that everybody has and that is dependency injection makes refactoring hard and the myth goes that if a child object now needs a new parameter then I have to pass it through all of its parents that's what the myth says or at least that's what people first think about when you ask when you say that all of the dependencies should be declared explicitly in the constructor however what what what this myth is is missing is that if you are passing something along just because there's some child object that needs it you're breaking the law of Demeter right because you are not supposed to know about anything that you don't directly need and so the myth says that you have now this that the dependency injection makes it harder because it makes you pass things around that you don't need but the law of Demeter says you shouldn't do this so let's see how we can reconcile this so first again let's have our example of house door and a doorknob and suppose that all of a sudden the doorknob so by the way first of all to instantiate this this tree you would have some kind of a factory and you would simply instantiate the doorknob you pass it into the door and you pass it in the house and now the house is constructed with all the parameters that it needs now suppose the doorknob all of a sudden means a color and the door needs a window or something like that right the myth says this is going to put pressure on a house because now house has to ask for all these parameters to pass it through but that's wrong right because the house isn't the one responsible for constructing these things and that's the key difference how simply says I need a door as a matter of fact houses emit is not even aware that door has doorknob so from a factory point of view and now I have to instantiate the color and a small window to fit inside of of the of the door but notice how it doesn't actually affect the house so contrary to what you would think by explicitly the asking for your dependencies in the constructor it actually makes it easier to add parameters not harder and the reason for this is because all of your business logic I doesn't deal with object lookup of the construction right the business logic simply says I need all these things and then somewhere else is a factory that's responsible for wiring the pieces together now at this point many people say aha but now I have one Factory per class that I have twice as many classes that's not true either notice again how one factory serves a whole bunch of objects typically you actually have in theory you actually only need is one factory for all of the objects of the same lifetime in practice we would really have ridiculously large factories and so we break it down further but nevertheless the point is you really only need one factory plus couple of break down to the classes are not ridiculously large per object lifetime and in typical application you have your long-lived objects you have your session if you have a web application your session scoped objects and you have your request scope objects plus you have some objects that are really short-lived typically those are rare you typically just stick to your request scope so in theory you can get away with four factories in such an application couple of rules about object construction so you only really want to inject objects whose lifetime is equal or greater than yours so a new constructor if you're asking for if your house and you're asking for a door it is assumed that the lifetime of the door is at least as long as that of a house if not greater you can get into some really troublesome spots if you break that rule so if you have an object that needs to have a shorter lifetime you typically pass that through a stack or you simply break your object graph in some kind of a different structure so that you group your equally little objects together so that you you simply instantiate although equally equal lifetime objects together and then you say now do the work that you need to do get done so you have to basically phases let's talk about other common practice that people do and that is paranoid programming so people love to put null checks everywhere for example in this case we have a house and there's a precondition check that says make sure that somebody whoever constructed you gave you a real door that seems like a good idea but it turns out actually that from a testing point of view it makes my test much harder to deal with suppose I have I have a house and I want to paint the house the color red and I want to assert that the the house color is red after I painted it presumably that has nothing to do with door so I should be able to simply say create a house and pass in an old door because the door is not involved for this particular method it's involved with other methods but not with this one it prevents me from actually doing this because I need to sex the the the precondition will throw an exception so these kinds of preconditions actually make it hard for you to instantiate your objects to put it differently I prefer an application that I know that it works because I have a whole bunch of tests to prove it that it works rather than have an application that I think it works because there is precondition checks everywhere executable checks in formula tests are always more valuable than having a precondition check now people will say well what if it's an external based API rules might be different you know your mileage may vary if you are really creating an API for an external world where people are going to be calling you it makes a lot of sense to put precondition checks inside of your methods but if this is your internal API that your application uses internally what you're really saying is that you don't trust yourself and so those checks tend to just hinder your testing then and make it rather than make it easier also as I pointed out earlier is if you come across a test like this and you see new house new door you're not quite sure if the door is involved in this particular test or not like is the door involved in painting you're not sure right and so by passing in a null you're making it clear to the user that door has nothing to do with the particular test I'm doing and that's I believe a valuable information to be passed on so I like to think of it this particular way that in production code right the code that I expect to ship I really don't want to get in the business of passing notes I always want to pass you know don't pass in at all rather give me a empty collection don't give me a null for lager rather give me a lager that doesn't do anything still in production code you always should assume that you're getting a real object that you can call methods on and if you for your particular configuration feel like that that particular object doesn't need to do anything then have a known value object that simply creates no offs for all the methods you call on it which should also that mean that you should not ever have to do no checks now that's maybe a stretch and maybe a nice gold get to but for the most part we try to avoid having null checks now in a test it's the exact opposite in a test when I instantiate objects I really want to be able to pass a null so the constructor to basically tell the reader that these parameters are irrelevant for this test you know there might be a different test where those parameters are not null and some other parameters are no but for this test it's irrelevant so in your tests your nulls are your friends but in production code it tends to be your enemy same thing for the new operator right if you instantiate an object in your production code directly then you're basically preventing anybody from giving you a mock a friendly or controlling the construction that's why we don't want to do a new operator in a constructor like we saw with the with the document and new HTTP client it's much better to ask for the HTTP client better yet forget the HTTP client ask the thing that you want which is the document so your production code really should not be should have all this be devoid of new operators instead the new operators need to all be inside of either your factories or inside of your tests because test is all about instantiating some sub portion of your application so that you can go and poke on it so in summary you want to abandon the new operator pretty much in 99% of your cases now there are certain places where new operator is perfectly okay instantiating a hashmap it's no problem with that there's no need to ask for it in the constructor just go ahead and instantiate same thing for collection instantiating what I would call a value object such as a user totally not a problem just simply make a user the key with these objects that they're all reliefs of your application graph like there is nothing behind the hashmap really it's the end of the road that's why instantiate it is perfectly okay but for the most part your services your objects that are responsible for talking to each other and collaborating those need to simply ask for the dependencies finally because you're not responsible for constructing these things and you're really broken your application down into the phase of construction in the phase where the business logic lives you should never be in the business of asking for anything that you don't really need all right this is the law of Demeter you should always be only asking for things that you need directly any kind of indirection that you place inside of your production code will make it so much harder for you to test your code from a from a test because now you have to instantiate all this intermediary object and maybe mock them out and so on and so forth and that cost becomes double the cost if that work is done in the constructor so what's painful in the method I can survive once what's painful in the constructor is going to be shared by lots and lots of tests all over your application so really you want to think of your objects as having come in to two categories there is your your pile of business logic this is the stuff that does work in your application this is also where probably most of your bugs are in your application you know this is where the if statements are and the the loops and you know collaborations and so forth you want to make sure that these objects are really easy to instantiate from a testing point of view and then you have your pile of factories or the new operators or the builders or so on and so forth now by separating the two pieces out you get an interesting from a testing point of view makes it really easy to test because you can always instantiate the business logic and passing its dependencies and test it that way and you can also instantiate a factory and ask the factory to produce a object graph and assert that the object craft is correct without actually running any kind of cool down there if you mix the two if you simply call the new operators wherever you feel like then it's really hard to say why don't you I want to test to make sure that the instantiation of the database works correctly but I don't want you to actually go off and call any kind of sequel methods on it so by having the separation you can actually test both the factories and the the business logic in isolation if you mix them together not so easy it's very easy to basically call a method that starts doing the real work instantiate more objects as it goes along and they do more work and so on and so forth so it turns out that our factories if you file the dependency injection the way it is recommend that it turns out that it's really really easy to instantiate things you simply look at the constructor and you say ah in order to instantiate a house I need to instantiate these other things and interesting she ate those are interesting so those other things and on and on as a matter of fact the whole process is so automatic that you can build a framework that simply uses reflection to look at the constructor and says and say aha in order to instantiate this I need these other things I mean this a normal instantiate those I need those other things and so on until you get a full closure one such framework is juice and with juice you basically don't have to write your factories you simply write your business logic that says I need the following services to get my work done and you assume that the juice can basically look at the stuff using reflection and say ah I know how to put the pieces together because simply the pieces just ask for each other so when you have these this builders in these business logic write small tests are become very easy to write and understand and we kind of pointed it out you can test everything in isolation and you can test the factories in isolation and you don't have to worry about the factory also doing the work or the the work also in tank shading other objects it also makes it so that because there is the set of factories that are responsible purely for wiring it makes it very likely that if your business logic works then chances are very good that the whole application works because the wiring especially with the strong strongly typed compiler you can't really plug things in the wrong way I mean you can and some circumstances but for the most part all the hard things are kind of compiled checked for you so in many cases it's sufficient to simply have test for your code and kind of assume that the whole thing works now you probably don't want to just purely assume and you probably won't have a couple tests to make sure the wiring works together but for the most part if I have tests that prove that everything works in isolation and I have a simple one simple test that kind of proves one happy path to the application I'm actually pretty confident that both the pieces work and the wiring is correct and it's probably working the way I intended to and I believe that's it so I am gonna ask four questions now no questions you guys are all dependency injection experts well I know you guys are alright well thanks for coming yes question can you grab a mic saw the null check thing you mentioned that my test you didn't need to create anything in tension anything that the code actually doesn't need mhm um so there's one kind of a testing fake called a dummy are you suggesting we will never need a dummy anymore no I'm not suggesting you don't need a dummy so dummy would be like your now value object correct is that what you mean by dummy dummy is something that only has the correct type it doesn't instantiate doesn't implement any real method right but it usually has no ops for all the methods right right right so it's essentially a null value object so if you have a class that expects to have a logger and you don't want to actually log anything I would pass in a dummy lager into or no value lager into it I'm actually talking about something slightly different when I find the example again with with the house and the painting here we go ah a house might have a whole bunch of methods for example house might have a lock method and house also might have a paint method when I call a lock method presumably house has to collaborate with the doors and the windows in order to get the whole house locked down so if I was to write a test for the lock method I would pass in a real door and real windows because I wanted to assert that the whole locking process works but if I'm painting the house that method has nothing to do with doors or windows so I shouldn't be forced to instantiate doors and windows inside of my tests for getting the whole problem of dummies even if I have a dummy door hanging around I still don't really want to instantiate and pass it in because they kind of suggested the reader of the test that somehow the door is involved in a whole painting process for this code example I understand but like what are the scenario you think will be a good case for dummy object you okay kind of thing you would like you would use a dummy object if you know that in your execution path you are actually going to be dispatching and the parameter which means a Knoll would cause a nullpointerexception but you really don't care about this particular object because it's something like a logger or locks or something of that sort in which case you just want a no operation to happen over there that's what a dummy is for the dummy is needed when I dispatch it but not now use it right and Knoll is a much stronger form of a dummy because it says not only is it not needed nobody's ever dispatching off of this thing alright guys thank you for coming if you have any questions do email us
Info
Channel: Google TechTalks
Views: 262,368
Rating: 4.8978724 out of 5
Keywords: google, techtalks, techtalk, engedu, talk, talks, googletechtalks, education
Id: RlfLCWKxHJ0
Channel Id: undefined
Length: 37min 57sec (2277 seconds)
Published: Fri Nov 07 2008
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.