Robert C Martin Clean Design, SOLID Principles I and II

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello yep looks like I'm turned on good afternoon Norwegian crowd so what energy fuels the Sun all right so somebody said helium somebody said hydrogen these are elements they are they might be fuels but I asked you what power powers the Sun what is the power that powers the Sun fusion fusion is the power the powers the Thunder fusion of hydrogen into helium okay and why does that happen in the Sun it's very high temperature wait now wait wait the fusion is the thing that makes the high temperature so why is the Sun fusing in the first place Big Bang no although that's a good guess you get Marx for guessing it what gravity someone said gravity very good okay now think about the Sun the Sun is a million miles in diameter 1,500 kilometers in diameter all right and it's made of hydrogen and it's fairly dense at the core of the Sun there's a small little area about 10% of the mass of the Sun and roughly a half of million miles of hydrogen surrounded on all sides pressing down with horrible weight the force of gravity is terrible in there it's deeply compressed and it's very hot simply because of the pressure and that heat and pressure is enough to cause a few hydrogen atoms to fuse not very many about 400 million tons of hydrogen fuse per second which if you think about it is not very much can you hear me back up there okay good if 400 million tons of hydrogen fused per second there's enough hydrogen in the core of the Sun for 10 billion years so the star will burn our Sun will burn at the rate it's currently burning for another Oh five or six billion because it's about five billion years old now as the fusion in the core heats the core the core tries to expand but the weight surrounding it tries to compress it so the star sits there like Atlas holding up the world in dynamic equilibrium unable to move expending furious amounts of energy and all of that is caused by gravity Fusion is just trying to resist and that's where all the heat comes from this is a war that the Sun will eventually lose and it will lose rather spectacularly about five billion years from now all of the hydrogen in the core will be gone it will start to fuse helium the helium burns at a higher temperature which cause will cause the Sun to expand the Sun will expand about a hundred times its size what's the diameter of the Sun I told you it's about a million miles not a hard number to remember right about a million miles in diameter it will grow by a factor of a hundred so when it grows it will be how big a hundred million miles in diameter good how far away is the Sun less than that it's a good chance that we will be inside the Sun on that strange day or perhaps we'll be skating around the outside of the Sun or maybe we'll have been clever enough to move the earth maybe out to the orbit of Jupiter that would probably be warm enough in either case this is serious global warming not the kind that we're debating right now our course today for the next hour or so is on the solid principles of object in a design this is of course I have been teaching well you can see the dates up there 1998 is the copyright actually I've been teaching this probably since 1995 finally someone's listening what is um what is object orientation we've been doing it now for a very long time so what is it what is all this oo stuff really what's it all about encapsulation good word now you get a point for that word what else abstraction good words these are good words yeah and capsulation abstractions great what else representation this is very good ok let me ask the question differently why do we do it why are we so interested in objects how come all of our language nowadays are object-oriented what makes us think that object orientation is a good idea what's that easiness what world are you living in easiness how many of you thought C was easier than than Java or C sharp how many of you thought VB before it had all that object stuff was easier it's much easier to do print you know progressed procedural programming all you have is if statements and while loops in a few functions and then they add all this complicated stuff inheritance and classes and private and protected and all this polymorphic stuff it's not easier it's complicated this oo stuff oh maybe it makes certain other things easier what might it make easier if we had all this complication inheritance and polymorphism and all that stuff we must be getting something back solving complex problems interesting do we solve more complex problems today than we solved before all this oo stuff came around well maybe we do maybe we do and is that because of oh oh maybe it is ok so what is it about oo that allows us to solve more complex problems let me ask this a completely different way what goes wrong with software everything yeah thank you yeah everything does you have a location and you dreamed it up and you thought of the design and you managed to get the design beautiful and you coded it and the code was lovely and then you shipped it and supported it and then over time the code rots it begins to degrade like a piece of bad meat right the months go by and the smells that come off the code get worse and worse and worse you can tell how old a code base is by looking at the face of the developer as he opens up the code on his screen why does this happen to software why does it rot it's not biological material there are no bacteria that rot it why does code rot changes changes changes by what changes of what requirements those doggone customers if they would just leave our systems alone that would work just fine but no they have to change the requirements on us and that makes our code rot why does it make our code rot why does the change make our code rot bad design uh-huh bad design of the change or bad design initially both well wait a minute now wait a minute wait a minute what changes are going to happen you know No so how can you design for changes you don't know you can't thank you for answering my questions so forthrightly not what norwegian are you so you can't design for changes that you don't know are coming you don't know where these changes are gonna come from and so the initial design cannot be completely blamed and yet we are responsible as designers as developers for creating systems that can be changed and it is changed that rots our systems and somehow we have to deal with that so now let me just let me do this a little differently what is the symptom of a rotten piece of code how do you know that the code is rotten what hey it's rigid what does that mean rigid difficult to change your boss comes to you and says I want I want you to make this change right there's a bug and we've seen this bug and I want you to fix the bug and and as you listen to the bug you realize that you know where the bug is you were in that module and not too long ago now you know the line of code where the bug is so you give your boss the standard estimate six weeks right you're not allowed to give an estimate less than six weeks because your other your other programmers will come to you with clubs so you'd say six weeks fine and he goes away happy he got the minimum estimate and now you you go to your laptop you open it up you go to the module you scroll to the bug there it is you see it it's on the screen and you change the code to fix the bug and you save the file and you compile it and you're about to execute it and you think wait if I change that line of code there's this other module over here that calls it I'll bet I'm gonna have to make a change over here and you open that one up and sure enough you're gonna have to fiddle with that one a little bit and then you think wait if I change that when there's these modules over here and you start the process of chasing your tail around the code six weeks go by seven weeks eight weeks go by every module in the system has been changed you haven't been gotten a clean compiled in weeks your boss comes to you and says I thought you were gonna be done with this in six weeks you say well it was a lot more complicated cater than I thought that's rigid that code is rigid one change leads to another which leads to another and what is it that causes this chain of change tight coupling dependency I've had the word on the board there for a good long time now dependencies dependencies between the modules is what causes that chain of change to take place through the code the thing that oh-oh gives us is the power to manage dependencies we could talk all day long about all the other things that ou might be good at like we might talk about how it's good at modeling the real world well maybe it is maybe it's not I don't care what it is good at it's allowing us to manage the dependencies between modules and we can manage those dependencies through an operation that we'll talk about a little bit later I call it dependency inversion where every dependency in the system can be turned around 180 degrees if we want to something that you cannot do in procedural programming but you can do in object-oriented programming in procedural programming the source code dependencies point in the direction of execution when you call a function the caller knows about the colleague but when you call a function in an object-oriented language the collie has no idea where it got called from and the caller does not know who it's calling we have been able to turn that dependency around to break it so we as designers and architects can choose which dependencies we wish to turn around at certain points where to apply inversion of dependency so that we can break the chain of coupling through our modules and that was all one slide I actually managed to talk about a number of slides here but I'll just keep on going we talked about rigidity rigidity is one of the symptoms of of poor design another poor design is fragile 'ti fragility is the tendency of the code to silently and mysteriously break in strange places when you change it in other places there you go and you find a bug and or you add a new feature in one module over here and some completely different part of the system breaks and for utterly strange reasons who's had this happen to them when the yeah when this happens managers look at us like we are fools wait what you changed that over there and that broke what kind of system is this how could that have happened how could you have designed a system where you touch it here and it breaks over there oh we are in terrible trouble it happens all the time but people looking on the outside in believe that under the hood things must be terrible and maybe they're right I worked on a system once where we had a whole bunch of terminals ascii terminals there were like 24 of them and and people would type menus on these things so you'd in order to execute the functions of the system you would type one to go to the first level of the menu and then a new menu would pop up and you'd type three to go to the next voice systems do this now computer systems usually don't but back in those days that was what was on our terminal deep down in the menu structure there was a misspelling the word progress was spelled with a c' process we had all seen this misspelling and we thought oh that's a shame but no one ever bothered to fix it and it lasted for years and then I happen to be in the module one day just by accident scrolling through on my screen and I saw the word their progress and I said oh and I changed it to process I compiled it and compiled I executed it it worked and then I made the biggest mistake of my life I shipped it what I did not know was that our customers guided by the wisdom of our marketing department had told had built systems of their own to hook into our terminal ports and execute our systems by reading the menus and interpreting them now forget the stupidity of this idea they had copied our menu structure into their code and of course they copied the spelling error with that so when I fixed the spelling error and their systems broke and all of our customers were calling at the same moment on the same day and our operators were going crazy and my boss came to me saying what the hell did you do yes I fixed a spelling error well don't ever do that this is fragile perfect a spelling error and then terrible problems occurred that is another symptom of poor dependency management yet another one is code that is not reusable of course the word reuses has gotten a very bad rap we used to think that oh all this oo stuff would give us reuse and then we found out that it didn't give us any reuse at all and and now people talk about reusing they have to mumble it under their breath I mean we tried to get reuse but yeah that didn't work too well anybody here ever try to write a reusable library sorry turns out to be a bad idea we didn't mean him it gets you into such trouble most reusable libraries did not turn out to be reusable most reusable libraries didn't turn out to be usable right we trended to think about them in great abstract terms and build massive abstractions and and then everybody looked at them said I'm not using that the there was a wonderful statement I think it was yarnís truth trip who finally made it he said the best aspect of a reusable library is that it first be usable and then once it's usable you might be able to get it to be reusable I've talked about rigidity I've talked about for agility I've talked about all this stuff I won't talk about the trailer maybe later dependency management dependency management is the practice of partitioning our design into all for lack of a better word I'll use components that partition our design into components and manage the dependencies such that the components don't depend on each other arbitrarily they depend upon each other in very strict ways and in very strict directions I use this idea of a brick wall where we can take the bugs and put walls around them so a bug in this part of the code cannot leak out into that part of the code we build firewalls in the source code structure of the system so that there's no way a change on one side of the system can propagate all the way to the other side of the system we manage our dependencies to prevent that from happening let's look at how a system might rot this is a very small system and we're going to watch it rot over a period of a few weeks we'll speed up time for you so that you don't have to sit here for a couple of weeks the case here is that your boss comes to you and says got a new function for you to write it's got a copy characters from the keyboard to the printer that's all now you think six lines of code so you give him the minimum estimate good six weeks boss goes away perfectly happy and now you start to think about this huh what is this system going to look like Wow and of course all good developers draw pictures it's a requirement of developers they must draw pictures before they do anything else so we draw a picture of this and we realize that well we'll probably need a copy loop just be a little module up at the top that loops around and what it's going to do is call the keyboard function and get a character and then it's going to call the printer module to copy the character to the printer and the copy loop will probably test for end-of-file that's Monday Monday we draw that picture and we'd like to keep working because we this took us 15 minutes but there's a new guy who started in our company we have to go train him it'll take all day Tuesday we write the code here's the six lines of code easy to write we manage to type it in takes us about five minutes to type it in we'd like to compile it but there's a bug in the field we have to go fix it takes all day Wednesday we compile it compiles first time good thing too there's some cross-functional meeting we've got to go to takes all day Thursday we test it works first time no problems at all takes us about 10 minutes to test it good thing too because there's some quality meeting we've got to go to takes all day Friday no meetings no nothing the whole schedule is clear we've got the entire day good thing too it takes us all day to get this into the source code control system we have five weeks left do we tell anybody no a lot of other stuff to do two meetings to go to and people to train and bugs to fix so at the six-week boundary we go to our boss and say I got this module done now I think it works and boss takes it we win awards for this code and hundreds of other programmers begin to use it it's possibly the most successful software that's ever been written at our company this six lines of code massive numbers of people are calling this code we have a big party about it it's wonderful oh the copy function sometime later our boss comes to us and says you know that program you wrote what a killer app you deserve that promotion you got for that now listen sometimes we need it not to read from the keyboard sometimes we need it to read from the paper tape reader anybody know what a paper tape reader is no well never mind that we need it to sometimes read from the paper tape reader so we think about it for a while and we give our boss the minimum estimate six weeks good he goes away perfectly happy and now we think okay how are we going to make this happen how are we going to make it so that you can call this function and sometimes it'll read from the paper tape reader at what we'd like to do I'll go back to the original what we'd like to do is put a boolean there boolean if true reads from the paper tape reader but if false reads from the keyboard but we don't dare put that boolean there we can't put the bully in there why not hundreds of other programmers are already using this code we can't change the signature of the function if they if we change the signature and that function they'll all have to recompile and retest and they'll all submit estimates of six weeks each and this won't get done for decades so we can't change the signature but what we can do is use a global there's always a way to get data into a function just a nice little global here called the G tape reader global we will initialize it to false the idea here is is that in the while loop if the G tape reader global is true we will read from the tape otherwise we'll read from the keyboard so that works nicely for us the best best operator in the C family of languages is the question call it an argument operator you can put whole if statements into an expression the idea here is that the people who want to use the tape reader must set the variable to true then call copy and then they better set it to false later because otherwise somebody else will get in trouble so we're going to cover our butts here by putting a comment in right remember to clear it no one can blame us that we didn't warn you sometime later our boss comes to us and says you know sometimes we'd like it to write to the paper tape punch well we've got a design pattern for this now we know how to solve this just another global another question mark colon operator we're on our way that didn't take six weeks but we can't tell anybody now this code is clearly rotting two changes to the requirements and already we've got a whole bunch of new dependencies we depend on the tape reader we depend on the keyboard we depend on the punch we depend on the printer and then the dependencies are accumulating every time there's a new change you can imagine what's going to happen over the next several months as our boss comes to us and says oh yeah sometimes we'd like it to read from the optical character reader and sometimes we'd like it to write to the speech synthesizer and we put more and more of these variables in and more and more of these questions that was my water that rebelled it knew that it had to get out of this company and that's what's going to happen once this module grows to about a thousand lines we're gonna look at it and go I can't maintain this anymore time to get a new job let some other guy take care of this sucker it's not the way it had to happen however had we thought a little bit differently about this module when our boss came to us initially we might have written it like that now in this case we're calling get char and put char this is old UNIX library stuff what does get char do in the old UNIX library gets a character from standard input and what standard input whatever but it defaults to the keyboard console oh so it works the same way as the first code we wrote and put char that puts a character to standard output which defaults to the printer so this code works exactly the way our first version did but this code behaves differently when our boss comes to us and says that he wants it to read from the paper tape punch because when he asks us to make that change we have an option we could tell him or we could say six weeks and during that six weeks we would have to do nothing because this will already read from the paper tape reader it will already write to the paper tape punch all that all that has to be done is for the callers to change standard in and standard out appropriately this code does not rot when the requirements change now what's interesting about that is that the difference between this function and the original function which was there is two words that word and that word otherwise they're identical why would changing two words utterly change the way this program responded to change why do those two words prevent this code from Rocking I broke the dependencies on keyboard and printer and no matter what happens as I add more and more new devices no new dependencies will be added to this module why what is so magic about that word what's so magic about the word get char that it prevents the addition of new dependencies it's the word as generic the word is abstract very good these are abstractions abstractions I have changed the copy function to use abstractions instead of using detail now interestingly enough this is an object-oriented program it doesn't look like one because it's written in C but I want you to think about what get char and put char really are they are polymorphic functions on a class named file inside that class name file written in C there is a jump table that jump table although it's not a C++ v table looks an awful lot like a C++ V table because it routes to the right device driver through the same mechanism than any other polymorphic dispatch would this is a polymorphically dispatched object-oriented program written in C and the reason that it does not rot it means because it took advantage of that polymorphism to change the dependency structure we're going to talk an awful lot about this kind of dependency structure over the next it went 35 minutes we're gonna go through a set of principles that talk about this kind of dependency structure we could rewrite this code in Oh some language that's kind of like c-sharp and kind of like Java but is neither here is the loop it's inside a class named copy there are interfaces named reader and writer that have read and write functions there is a constructor that takes a reader and a writer and our loop has no idea which devices it is using it's simply reading from the reader and writing to the writer the dependency structure looks like this the copy code depends on interfaces the device drivers depend on the interfaces as well compare that to this where the copy program depended directly on the keyboard and the printer notice the direction of those dependencies the drivers were dependent upon whereas here the drivers do the dependent this is inversion this is dependency inversion where we take the lowest level details that usually would be dependent upon and we turn those dependencies around so that they do the depending instead of being dependent upon this inversion is the key to keep code from rotting so now let's take a look at some of the principles that take advantage of that particular kind of dependency management there are five of them but we're going to talk about today I hope I get through them all they if you spell them out in a certain order they they create the word solid we didn't know this until a few years ago Michael feathers one day just said you know if you switch them around a little bit they spell solid so we switched him around that seemed to have been a good idea the first one is the single responsibility principle the single responsibility principle says that a class should have one and only one reason to change now maybe we should have called it the single change principle or or something like that but the name has been chosen a long time ago I don't think we're going to change it anytime soon the single responsibility means that this class employee should only have one reason to change now take a look at that class employee it has a number of interesting methods the calc pay method which computes pay for employees these are the business rules and then we have the report hours function which builds a report for the accountants people of the hours worked by the employees this this function here returns strings in a certain format that can be added in to a report and finally the right employee function which saves the employee to the database how many reasons does this class have to change three and those three reasons very simply if the business rules change this class will change if the format of the report changes this class will change if the schema of the database changes this class will change those three things are utterly unrelated the schema of the database the format of the report and the business rules that calculate a completely unrelated but we have coupled them together by putting them in the same class so that when the business rules change everybody who calls the employee to get a report out will be affected when the format of the report changes everybody who calls the employee to calculate pay will be affected how are they affected they're affected in the build the build will force you to recompile classes that depend upon employee and if any of the methods of employee change all the incoming dependencies force a wave of builds to go out deployment if you'd want to put these classes in specific dll's and deploy them independently anybody depending upon Kalka pay needs to be deployed with the employee anybody depending upon remote report hours needs to be deployed in the named dll or at least at the same time so we have a a wide range of incoming dependencies because employee does too much now you think yeah but wait a minute these are perfectly good methods on class employee isn't this where we're supposed to put methods aren't we supposed to put methods into objects with which deal with the variables of those objects the answer is yes of course you are but you also have to put them in classes that can be independently deployed and that don't cause cause massive build thrashing so what we want to do here is split this class up into three different classes one that calculates pay another that reports hours and another that writes employees does that mean that you have to have a separate class for every method no you need to have a separate class for every group of methods that changes for a different reason if you've got a whole bunch of reports that all have report formats that they control well you'll probably put all of them in the same same area same class or same group of classes if you have a whole bunch of business rules you'll put them in the same class in the same area if you have a bunch of database stuff you'll put them in separate classes I would like to see this class split up into three what I would like to see is an employee class which knows how to calculate pay a report class which uses the employee and knows how to format the report and another class entirely that uses the employee and knows how to write the employee to the database I don't want the employee object to ever know that a database exists I don't want the employee object to ever know that a report exists I want the employee object only to know that the business rules exist you can see what I'm after here we have a payroll application payroll application uses employee it does all the calculation of pay there are probably many methods involved with calculate pay not just one we've got a report writer that uses the employee and it writes the report we've got an employee repository which uses the employee and writes the writes the employee out to the database and now when the format of the report changes that class changes but not this one and not that one when the database schema changes this class changes but not that one and not this one and when the business rules change that one changes and maybe there's a little bit of impact on those two we might even want to take these out and make them separate just to keep them all isolated from each other that's the single responsibility principle and someone once said that you could you could describe all of these principles just by twisting the single responsibility principle around by the way I think that's true of all these principles you can phrase each one of them in terms of the others because they're all really driving at the same fundamental idea manage your dependencies somehow the second principle it's called the open-closed principle and it's probably the oldest of these principles it was invented by Bertrand Meyer a good long time ago I think it was 1988 and I don't know if the word invented his right or perhaps the word discovered who knows who Bertrand Meyer is a few of you who's heard of the language eiffel more of you he invented the language eiffel okay he also wrote one of the best books out there on software development in the 80s it was called object-oriented software construction a wonderful book that described a whole suite of wonderful principles he used the language eiffel in that book which was a bit unfortunate because nobody knows the language but the stuff in that book was very very good very high content one of the things he said in that book was this he had this principle called the open closed principle which says modules should be open for extension but closed for modification open for extension means that you can change the module closed for modification means that you don't have to change the module wait open for extension means that you can add things extend you can change the behavior closed for modification means you can do that without changing the source code now that sounds weird how can you change the behavior of a module without changing its source code but you can we've already done it with the copy program the copy program had a nice little loop in it and that nice little loop had no idea that it was going to be calling the paper tape reader or the printer or the keyboard I was able to create new IO drivers and change what that high-level loop did and I didn't even have to recompile the high-level loop I could change what it did I could extend its function without modifying its code that's what the open closed principle is all about let's use the ability of polymorphism to take one app one algorithm and set it aside so that it doesn't have to change when these things over here do then we can extend what this module does by adding new derivatives of something abstraction of course is the key when you're doing it when you're trying to follow the open-closed principle abstraction is the key imagine that your entire code base followed this principle perfectly this by the way is impossible but let's assume that it could if you could get all of your code to follow the open-closed principle then when you had to add a new feature you would not have to change any existing code you would just add new code because to extend the functionality of your system would not require you to modify any of the existing classes you could extend without modifying so if you were perfect at this every new feature every new addition everything you did would be done by adding new code and not by changing old code now it's not possible to get a system - fully conformed to this but what if you could get it to O 50% conform to it that would mean that when you added new features most of the code would be a new code not fiddling around with old code how many of you have gotten completely lost trying to add a feature to a massive system by fiddling around with 500 different modules wouldn't it be nice if you could just put it all in one module but there's the new feature goes in one module mm-hmm so let's see how that might work the typical connection between two parts of a piece of software two modules and software looks like this the client calls the server and if the client calls the server and depends directly in the server then any change to the server impacts the client now what if I've got a new feature I want to add and it's a new way for the for the client to use the server some different mechanism in the server that the client can use I could do that by putting an if statement in the client and having it call a slightly different server or I could do it this way I could create an abstract server have the client call the abstract server and derive all my two different servers from the abstract server if I do it the first way I must change the client I must put the if statement in if I do it the second way the client remains utterly unchanged how many of you saw a michael feathers talk yesterday where he was talking about small talk and the way boolean was implemented right this was a fascinating discussion because he demonstrated that all if statements in small talk made no decisions at all they just polymorphically deployed to a boolean class that called either the true branch or the false branch the decision was not made at the time of the if statement the decision was made at the time the boolean was created much different time it's very similar to what's happening here here we would make the decision every time we wanted to call the server here the decision is made when the server is created let's say that I I create a completely new kind of server it does it does the saint's follows the same interface but it does a whole bunch of different things following the same interface the client is not really unaffected client doesn't even need to be recompiled doesn't need to be redeployed if you do it well you can just ship a new server this is the open-closed principle now let's take a look at it in some detail and let's see let's look at I think I've got a couple of different languages here no idea what language is this who recognizes this app it's definitely C++ because it's got an enum I don't think C has an enum or does it does now okay I'm using struct well maybe it's C maybe it's C++ who cares it's one of those languages I've got a file here named shape dot H shape dot H begins with the enum the enum is of type shape type there's a circle and square and numerator there's a data structure in here name shape it has one element which is the ANU I have a file named circle dot H it has a struct circle it begins with an enum just like the shape did begins with an enum we're gonna use this to our advantage shortly this is the old C hackers trick to make different data structures look alike the data structure also has a radius and a center point and it fines a function called drawcircle which takes a pointer to a circle so you want to draw a circle you create one of these things and you pass the circle in to draw a circle there's a file called square dot H it has a data structure named square an enumeration named shape type same as all of these enum shape type it defines the side on the top left point and a draw square function as before and now we have the draw all shapes function the draw shapes function lives in a file named draw shapes dot C which pound includes all of these I use pound include in this example because in C++ that is a very very tight coupling in c-sharp using in Java import they're not nearly as tight but they are still bad enough this draw shapes takes a list of shaped pointers it loops through the list of shape pointers it asks each data structure in the list what it's type is and if it's a square it calls draw square if it's a circle it calls draw circle now what kind of changes are likely to occur to this application add a new kind of shape triangle okay let's add a new kind of shape a triangle and what's the first line of code we're going to change that one we're gonna add triangle to the enum which lives in shape dot H who pound includes shape dot H everybody so everybody has to recompile everybody has to be redeployed there's something fundamentally sick about the fact that circle has to be recompiled because we're adding a triangle something very wrong with this this is rigid the fact that we've got to make changes in lots of places and rebuild a whole bunch of stuff is rigid it's hard to add the triangle we're gonna have to add the 't triangle data structure then we're gonna have to go over here and add the triangle case now you might think well that's not a big problem but in fact it is a big problem because this is not the only such switch case statement in this system this one is draw all shapes but there's also drag all shapes and race all shapes rotate all shapes shift all shapes shrink all shapes there's dozens and dozens and dozens of functions and they all have this switch statement in them except that they don't all have that switch statement in them because some of the programmers didn't write it as a switch statement some of them wrote it as an if-else statement and some of them may have written it as a switch statement but changed the ordering of these and then there were the clever programmers who realized that they didn't have to put all the cases in for example rotate all shapes how do you rotate a circle you don't we're going to have to go through every one of these determine how logically to add the triangle and we're going to fail at this this is fragile it's highly likely that it will break it's fragile but the worst case the worst situation is not the fact that it's rigid and fragile the worst situation is this our business has suddenly decided that they're going to give this away for free but they're going to charge their customers for squares and circles they want to put square in Circle in separate dll's and charge money for them but they'll give you this DLL for free and now you as programmers have to tell the business we can't do that because this code depends directly on that code there's no way to put it in separate dll's or if you did put it in separate dll's there's no way to deploy them separately separating them into different dll's would be worthless they're not independently deployable that's the real hardship here because it affects the business model now we can't charge for circle and square we're going to have to change the structure of our code and how do we do that how do we do it like this even in C++ it's possible to do this we're just going to create a new class called shape it will have a draw function that draw function will be abstract that's what the little equals zero means and C++ means abstract will have a square class that derives from shape it has a draw function a circle class derive the shapes got a draw function and now are you ready for the lie here comes the big lie draw shapes we'll take a list of shapes it will loop through the shapes telling each shape to draw itself and life will be wonderful now let's go through the rigid fragile and deployable scenarios when we add a triangle to this what's the first line of code we're going to change here nothing nothing no code here changes when we add triangle Oh code changes somewhere somewhere in the system someone must create the triangles none of this changes none of this has to be recompiled this is not rigid and it's not fragile because all of the functions like draw and rotate and scale and erase and shift will all be methods of the shape class there's no way you can miss any of them the compiler will compile unless you implement them so it's not going to be fragile either and best of all I can put square in a DLL circle in a DLL draw shapes in a DLL and I can ship them independently of each other it would be perfectly valid for me to put this DLL and that DLL into the end of the whole application and run it with just squares or run it with just circles or run it with just circles and triangles but not squares and it would execute just fine so it's independently deployable this is the benefit of the open closed principle but I just got done telling you that this was a lie why is this a lie this looks nice doesn't it I mean this is a nice real-world bottle we've got shapes and shapes might be squares and circles this is this is a nice model it protects us from what what does it protect us from new shapes what if that's not what the customers change what if the customers come along and say yeah we really like this but we'd like you to draw all the squares first and then all the circles second we didn't expect you to make that change that's gonna chant that's gonna mess up everything now I'm and they have to have two loops and I'm gonna have to put if statements in those loops and the instant what are the if statements gonna be they're gonna be they're gonna be instance of statements they're going to be dynamic cast statements they're going to be testing whether or not this shape is a square and I'll do that one first and then they the customers chose a change that was not in line with our model our model protects us only from one thing new kinds of shapes if that's not the way the customers decide to change it then this design buys us nothing in fact it gets in the way this is the fundamental problem with all object-oriented design in order to do it well you must be able to predict the future and none of us can predict the future so we wind up trying to figure out what changes are likely to be made and put abstractions into our applications we hope one day that some of them will be made and then we lie to ourselves we say Oh a change happened that we expected we're so happy we put those abstractions in ignoring the 7,000 other abstractions that no one has ever taken advantage of but that we must maintain so I recommend a slightly different view I do not want you to go thinking in your mind oh I must follow the open-closed principle and therefore I must think of every possible change that might happen and implement all the abstractions forever and ever that will prevent this change what I want you to do instead is take a very pragmatic view imagine that you are a sergeant in charge of a squad of soldiers and that squad of soldiers is looking out over a field where the enemy lives and you don't know where the enemy is somewhere out there right you don't know where if you just knew where you could concentrate your fire in that direction so you say Johnson stand up and then you know where the fire is coming from and you concentrate your fire I want you to write your applications as simply as possible I want them not to take advantage of every possible abstraction in the world I want you to write them simply and easily and then put them in front of customers as soon as possible users and testers so that they start saying oh I think I want to change it this way and that way and then you know where to put the changes then you know where to put the abstractions the the abstractions you create protect you from changes that have already happened because those are the most likely changes to happen again when are we done with this one quarter - sounds good to me the Liskov substitution principle the Liskov substitution principle was invented by Barbara Liskov in 1988 I won't quote the real principle because it uses a lot of mathematical terms instead I will paraphrase it by saying that derived classes must be usable through the base class without the need for the base class to know the difference another way to say that is is that when you create a derivative it's got to be substitutable for the base any place you use a base you should be able to use the derivative it's a very simple idea but it has very interesting ramifications here is the first one imagine that I have a class named rectangle what are the fields of rectangle height and width and what are the functions of rectangle set height set width fine and imagine that we've had this class for Oh several years and we've been very happy with it and then our boss comes to us and says you know sometimes we need a square okay fine what is the relationship between square and rectangle Oh everybody knows that a square is a rectangle and if you've been studying software for any length of time you know that is a means inheritance so Square will inherit from rectangle because square is a rectangle now right away there's problems with this right away how many variables does a rectangle have do how many does the square need one how many will inherit two something's wrong the square is too big it's got too much data in it but we can hide that we can hide that we can we can implement the square set width and set height functions to make sure that whenever you set the width on a square it also sets the height what functions are going to be inherited from rectangle into square set height and set with what does that mean when you go set width on a square what does that mean set side wouldn't it be nice if there were a set side function up here oh no it wouldn't we don't want to set side function up in a rectangle we would like a set side function down in square but what we get to inherit is set height and set with this doesn't fit very well and the abstraction doesn't work very well but we can force it to work by doing this and having done this we pride ourselves by saying okay we've managed to clean up this abstraction it works there's no way for a square to ever be not be a square there's no way for a rectangle never not to be a rectangle everything's fine and now we have some poor user some guy over here who uses rectangle and this poor user of rectangle calls set width does he have the right to expect that the height doesn't change yeah he's calling a rectangle you call set width on a rectangle the height should not change and yet if we pass him a square the height will change and he will corrupt his heap and a billion instructions later he will crash and you will have to get a logic analyzer out and debug for weeks and you will finally find as you were debugging for weeks and weeks you will find out that the reason the system crashed is because somebody passed you a square and what are you going to do when you realize that someone passed you a square what code are you going to put in your system to protect yourself from someone passing you a square an if statement and if statement with an is I in it if this is a square which means you're going to hang a dependency on square violating the open-closed principle this this violation of substitutability the fact that square is not really substitutable for a rectangle will wind up forcing you to write an if statement which will violate the open-closed principle and you'll wind up with software that must be changed and that is rigid and fragile and cannot be deployed why did this happen isn't a square a rectangle yeah it squares a rectangle that's not a square that's a piece of code this is not a rectangle that's a piece of code they're not squares and rectangles they're just representing squares and rectangles now when you get a divorce you have a lawyer representing you and your spouse has a wife representing them it is not the two lawyers that are getting a divorce these are the representatives of a square and rectangle they are not squares and rectangles and so the relationship is ah between a square and rectangle does not hold between the representatives what is this relationship really this inheritance relationship is it really an easel relationship it's better to think of it as a behaves like relationship let's take another example I'm much more much more interesting one consider the class integer how many bits in it yeah depends okay pick a number 32 good its 32-bit integer now are there real numbers in this system an integer is a real number so we could have a class named real and integer could inherit from real how is a real number represented while it's represented as a set of integers there's the mantissa and the exponent and a couple of different signs but that's okay we could still represent thee now the all of that representation would be inherited into the integer but okay fine every real number is a complex number so we can have an inheritance relationship from real to complex what's in a complex number to real numbers the real part and the imaginary part go ahead and write this code but see if you can compile it you can't it will go into an infinite compile loop because of this self reference there is no way to express that in code you can't express it in UML just fine you just can't express it in code which also sells that UML is a language that doesn't correspond perfectly to code you know that you are violating this principle if you write derivatives that try not to do what the base class does there are functions in the base class that you don't want called in the derivative that's what's going to force people to use if statements later on if you have a function in a base class and in the derivative you rewrite it to do nothing sneakily hiding that it even exists eventually someone will have to write an if statement or if you are more overt about it and you write a function in the base class and then in the derivative you write it to throw an exception don't call me other people will have to write if statement so that they don't get the don't call me exception all of these are violations of this principle in short if you ever try to make a derivative that can do less than the base then you are violating this principle and it is not at all obvious on the surface that there are these substitution violations you have to study them very carefully I think we have time for one last principle which is called the dependency inversion principle which is the principle around which I like to rotate this whole talk remember the copy program which had dependencies on read keyboard and write printer remember how we solved the way that it rotted by taking those two dependencies right there and inverting them so that they pointed away every object-oriented program is constructed this way it has dependencies pointing towards abstractions notice how all these dependencies point at the two abstract classes the high level policy points at the two abstract classes the low level details point at the two abstract classes that is the dependency inversion principle and it tells us that whenever we have a dependency pointed at something abstract if we ever mention the name of a class make sure it's an abstract class if we ever call a function make sure it is an abstract function if we ever override a function make sure it's an abstract function we are overriding and don't don't reference a concrete class don't call a concrete function don't override a concrete function if you follow this principle 100% now you can't follow this principle on a percent it's impossible because at very least the new keyword is going to force you to mention concrete classes but you can structure most of your codes so that it is it looks like this instead of that and only one module in your system main can look like that and then it can call this part this part of the system and never the rest of the system pretends as though only abstractions exist and it is quarter two so I think we need to stop is that right so it stopped at quarter - yeah well okay it's quarter - we have to stop thank you for your attention I'm going to be discussing component principles here which is the next batch of principles in about 15 minutes I think yep okay talk to you guys see you guys later [Applause]
Info
Channel: Rakesh Jaiswal
Views: 9,462
Rating: undefined out of 5
Keywords:
Id: A6ZqNQdJPjc
Channel Id: undefined
Length: 64min 28sec (3868 seconds)
Published: Thu Jan 02 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.