Object-Oriented Programming is Embarrassing: 4 Short Examples

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I recently posted a video called object-oriented programming that's bad in which I made the theoretical case against object-oriented really never do it and a number of people complained about the title suggesting that it was somehow clickbait II but I disagree I chose the title because I think it's really true I have nothing nice to say about object program I don't like it I think it's really been a disaster for the industry and so to follow up that argument and see it through I wanted to present some concrete examples and this one's upping the ante so it's called object-oriented programming is embarrassing in this video I'll have four examples but they'll be really short less than 200 lines in each case two of them are in Java and two of them are in Ruby and I'm going to show how if you rewrite them into just a simple procedural style there's only code is much better and I'll try to discuss exactly why and just to make the comparison fair for each example I won't change the language so from Java I'll rewrite it in Java and if it's in Ruby I'll rewrite it in Ruby be clear I did not try to pick these examples at all I just basically took the first example that found on YouTube or Vimeo of some speaker giving a talk explaining how object-oriented design is supposed to work and how it's going to make your code better these are literally the very first code examples I came across when I started looking for them so if you think the examples are unfair do send in suggestions also I would want to follow us up with even longer examples because I know people are not going to be convinced but just to underline example is going to say oh well object or approach means virtues don't really come out until you're talking about much larger samples of code because it protects your code from becoming spaghetti when it gets larger and larger ideally we would actually put that theory to the test and take a 500 thousand line program and rewrite it from object-oriented code into procedural and vice versa and see what we get for obvious reasons though no one ever does that but what I would like to do at least is next take an example of say a thousand or two thousand lines and then next maybe some example that say ten thousand lines and ideally I don't know we write in like 50 thousand lines is a lot of work and I don't know if there's an ideal candidate for it but maybe one could be found I would be a long term project probably in the meantime if you have some suggestions of some code example you found some github repo of a complete program or maybe even just some sub a little program that's like 1,000 to 2,000 lines or 10,000 lines I'd very appreciate if you sent me some suggestions preferably it would be something in Java or JavaScript or Python or Ruby even though I prefer Python it's not a strict requirement but it's better if it's in language that everyone knows because I want to have it be accessible to as many people as possible so probably probably preferably JavaScript though if it make sure it's an example that is actually in an object-oriented style not prefer to read up to going to stop of actual object oriented style so before getting into the examples very briefly I just want to recap very very broadly my argument against object-oriented programming and this is a new formulation I suppose than what I originally stated in the video but it's another simple way of thinking about it and that is just let data be data what object oriented programming demands is that we're supposed to be conceptualizing our data as not just data but as some sort of thing with responsibilities and something which acts in the world to basically have to imbue our data with intent and agency and that's just a fundamental mistake data does not do things data is just inert and that's the way it should be and you're just adding confusion if you try and conceptualize your data as some kind of actor the other side of the coin is that we should let our actions just be actions we shouldn't have to conceptualize all the things we want to do in code in terms of some kind of data we shouldn't have to now defy all our verbs it is this conflation of action with data and vice versa that I think makes object-oriented programming so mystifying if you go down that path if you follow the advice of the object oriented programming gurus I think what happens inevitably is that writing code then begins to feel like you're having to solve the problems of analytical philosophy and platonic essentialism you have to constantly question the nature of the entities you are creating in your code if you create a chair class for example you have to consider the true nature of a chair you have to to determine it's essential aspects because if you get that wrong according to these gurus if you draw the boundaries around this entity incorrectly you're violating some some principle or another of object-oriented code it's going to bite you in the ass in one way or another and your codes going to become spaghetti and all the guarantees they promised about what object-oriented programming is going to deliver for you all their guarantees are void and you can't hold them responsible because you didn't follow one supposed principle exactly as they intended notice then the justification for object-oriented programming the circular you try and do object oriented designs and they don't seem to really quite working you understand why things are supposed to be arranged in this way and what benefits you're supposed to get well it's because you're just not doing it right so here are 10 more principles and guidelines in other words so the first example is from a talk given by Sandi Metz called solid object-oriented design you can find it on YouTube the centerpiece of the talk is a really short class that processes a patent it downloads patent file it parses the file and then it updates that patent information in a database and the core thrust of her talk is she's attempting to demonstrate the so-called single responsibility principle this idea that your classes and generally also your methods should only be doing one thing what exactly does it mean for something to be doing just one thing and one thing only well that's a disputed point the definition given by Robert Martin aka Uncle Bob who coined the solid principles and SRP that's the single responsibility principle his definition is that a single class a single unit should have only one reason for change now what exactly is one reason for change well it's something he explains the length I think it's even after explanation I think it's a very nebulous idea which is part of the problem but putting that aside for the moment let's look at her example class as you can see it's written in Ruby and it has four methods starting with run which is really where all the action is that invokes the other three methods download file parse and then update patents sandy however is not satisfied with this design and says that you shouldn't be either because what's going on here is we just have too many responsibilities and one class I actually broadly agree on that point it's very strange that this one unit of code is both downloading files but then also parsing and updating the database it's mixing a lot of disparate stuff and it's one unit of code that yes do all concern patents I suppose but there is something really weird going on here as evidenced by the simple fact that you know classes are supposed to be data types and yet look there are no fields in this class so what the hell is this thing really the real answer is that it's just this tiny namespace and that's all that's going on here that's all this class really is except it's worse than that because in object orientated so we have to manage this instance thing in a totally nonsensical way we're managing this instance that has no persistent value whatsoever and so it's you immediately dispose of it after invoking the run map what what is this thing why does it have to be a thing it serves no purpose being a thing Sandy's prescription is given this nonsensical class the solution is as you might expect more classes in her rewrite she ends up with first this config class which separates out and effectively generalizes the configuration concerns of this program which which is a sound idea I think but we have this constructor which the initialize method which takes in some set of options potentially I don't know exactly what purpose they serve but anyway so the options tell us where do we get the file name I believe for the CMO file that has configuration stuff that's loaded and from the yellow file we get this data which is then there's some weird made of business going on here in Ruby in her defined methods for environment method the names are turned into effectively properties of the config class itself or other actually methods of the config class itself that return values corresponding to the key value pairs of the configuration file effectively in the end we get a config instance that has a method for all the configuration property names and you just invoke the method to get its associated value so then my objection to this class is why do we need it at all why can't we just have a hash map instead of having a constructor we have some function taking in the options doing all the same business parsing the CMO file but why not just return the hashmap why do we need this class she also moves all the downloading business into its own class called FTP downloader and a similar absurdity leaps out here why is this a class we're initializing this config property which we're never in use again any user of the FTP download is never going to access this config property it's just they're going to call this download file method this you're creating the whole instance of a class just to call this one method why do we have a class why is this not just a function lastly here's a rewrite of the Patton job class the difference is there's now no downloader method instead in the constructor we create a config object and then pass that to create a downloader object and this downloader object is preserved in a downloader field which is then later used when we call the run method and I'll note here I'm surprised she doesn't use constructor injection as andö she has our Patton job actually creating these config objects and downloader objects as much as I don't like object or programming if you are going to do it I think for very good reason you do use dependency injection because that well that's a whole tangent but basically it solves the problem of having to wire together all your objects whether it's a much more sane way of doing it so I'm surprised she doesn't do that here it seems wrong that she's instantiating these objects inside this other constructor anyway that's a moot point because the code should just be this it should just be straight perceives with code just a bunch of kernel methods as they're called in Ruby basically just functions we don't have to ponder the significance of any classes and speculate as to what their fields might signify why they exist and what purpose they serve and so say instead of a config class we just have this get config function which returns a hash with all the fields parsed out of the ammo file and a story we don't have to think about any constructors and we don't have to do silly things like creating objects immediately call in one method and then never using the object again we don't have to do silly things like that in fact I would go a little further here I would take these little tiny functions that don't get Kol anywhere else except in this one place in the process patent function and just put them in line there maybe speculatively in the future as the program evolves others might become larger more involve operations where maybe at some point it'll make sense to split them off into their own functions because they get really involved but as it stands it seems really silly you know if I can come across your codebase and encounter just three functions I have to understand instead of five well of course at this tiny scale it's no big deal either way so it doesn't really matter but if you can show me a code base where instead of 100 functions I have three fifths of that that's just way more tractable it's just a lot easier to get a foothold and start understanding the code because I don't have to contemplate the potential uses of these functions and where they might get cold the next example comes from a video by Derek banos from a few years ago it's this whole series he has on object-oriented design there are 11 or so videos but this code comes from the second one of those to be fair to Derek his other videos on programming the more recent ones I've seen are actually quite good but this series is terrible because just the nature of what he's trying to teach it it's just disastrous so in this example he is writing a coin flipping game yeah you know one person throws a coin into the air and then someone else calls it heads or tails and there's a winner it's not going to actually really be an interactive program it's just we're simulating the result to get the end result you know so one of two names players wins and randomly it's either heads or tails and that's it and we just print out so and so one with a coin flip of heads or so and so on a coin flip of tails and that's all we're going for here in this video he's trying to demonstrate how to do formal modeling using UML techniques UML was a stanford unified modeling language universal model something like that who cares it's garbage as you can see by this this is his use case as its class diagram he has there's three classes there's a player and a coin and a coin game and you know the class diagram is one thing but then there's the actual composition of of instances so you have an object model which is different and then we have a sequence diagram which is basically just sort of like a visual representation of a progression of time and what talks to what and then from all that he writes his actual code so we have four classes here that the fourth class is just a coin flipping game in the bottom right which is just a container for because he knows Java and everything has to be a class I'm not even going to walk through this code it's totally confused even for something so simple because just here this is all it needs it doesn't really need classes of any kind except for container for main and then in this main okay well we'll split out play game to another method I suppose it doesn't really matter in this case we just take in two strings we randomly pick one of them and then we randomly pick heads or tails and print out so and so one with a flip of whatever and that's all we need to do and then we have this in a loop because that's what he did the user is prompted do you want to play again and you hit something and it just goes again that's all I don't think I have to actually make the argument about why his code is so absurd just look at it and then look at what you actually just needed to do at a minimum the lesson here is that UML is just garbage don't ever use it it's crazy if you want to plan out your code ahead of time that you know do so to a degree of course just to plan out your data structures right a little bit of pseudocode don't try and diagram anything that never works it's just a pain and he has to deal with all the tools for modeling the boxes and the arrows and everything they're all terrible and even if they were good it's just what's stupid about visual representation of code is that passed a very tiny amount of complexity you have all these boxes and then you need to draw a bunch of lines between them and very quickly you have so many lines that it's really hard to visually parse and why didn't you just write code to begin with because code has the nice virtue of you know you have a bunch of functions and they can all just refer to each other by name it's past a certain point of complexity of things all tied together it's better represented as just text and renamed references that's the main reason why visual languages have never really gone anywhere it's just a fundamental problem the next example is another one from Sandi Metz called all the little things I don't mean to pick on Sandy is just I found one of her talks and so it was easy to find another in this talk she presents an example of some ugly code some ugly branching logic and she demonstrates how this ugly branching can be more cleanly represented with polymorphism so looking at this actual code this was not a real code example clearly I think it was an arbitrary example and these are like random old Warcraft references apparently and looking at this I think we can all agree that this is ugly coat you don't want to see code like this if you're going to have a lot of complex logic it should be complex for a reason and you can very quickly see in a few places that this doesn't need to be so complicated this can be cleaned up definitely but here's her solution she puts everything in this class gilded rose I don't know I don't understand what that's supposed to signify but whatever and inside we have this class item with a tick method in sort of an abstract class item with an empty tick method and then we create three specializations of item we have normal brie and backstage each with their own tick methods because what you determined with the original branching code is that we really have four mutually exclusive cases we have the case where we do nothing we had one case where the string was equal to normal in other words equal to a spree and in other words equal to backstage passes to a TEFL 80 etcetera concert again some kind of World of Warcraft reference there so at the bottom here she has default classes item but then there's a specific cases she has this hash map normal Maps the normal class eight spree to the brie class and backstage yadda yadda to the backstage class and then at the bottom we have the self dot for method I think the significance of self here is that this makes it a basically a static method of gilded rose it's a class method rather than national instance method see right gilded rose dot for and you pass in the name which is going to be a string like normal and then the quality and days remaining and you get back an item object upon which you call it stick method and you get the same branching behavior we saw in the original code so all of this is clever I suppose it's certainly making full use of Ruby language features but if you don't care about doing that why didn't you just write this code the problem with the original code the reason was so ugly is because it didn't correctly first identify the four major mutually exclusive cases and if it had there's no reason it can't be expressed as a switch we're in a fell slaughter the way she presents it in the talk is that polymorphism swoops in and saves a day it makes the code much cleaner but the there's no need for it you can just write a damn switch if you can figure out that the major cases are the string being normal or age degree or back stage yada yada then you can just as easily write this code you don't have to involve polymorphism why would you want to introduce more concepts of datatypes into your code when you don't need them that's just perverse the procedural code version is just as clean and actually much cleaner because it doesn't involve extraneous concepts in fact I would actually make this a little bit more procedural here we're still using instance variables as if this is the method of some class but on a straight procedural version we don't really have classes so we do pass into tick we pass in a name and then some object and then operate upon its members now I've seen this polymorphism can replace all branching idea in other places and the larger argument generally given is okay so in your code you tend to have a switch over one piece of data and you're going to have parallel switches in many other places in code and so it's better to express your switch logic polymorphically because then especially in a static language at least you would be required by the language to make sure you have all the methods implemented in Java for example you if you implement an interface and you don't implement one of the methods that's an error so you're less likely to forget to cover all the parallel cases when you introduce a new case with straight switches you might update half the switches and then forget that that the other half but of course that's in a static language and a dynamic language like Ruby if you subclass some abstract class and you fail to override one of its its methods you're not going to get any language warning whatsoever so it seems like a really weak argument to me in a dynamic language regardless I still think polymorphic branching is a perverse idea mostly because I think these neatly parallel switches are actually quite rare much more commonly I think you have scenarios where the switches are semi parallel like they switch over one set of factors in one part of code but then the branching logic needs to be quite different in other parts of code so going through all this trouble to Rican Sep Chua lies certain switches as being switching over types when you don't necessarily have different types that's a lot of bother and conceptual overhead for something that's quite likely to not pay off in the long run the last example is from a talk by Robert C Martin aka Uncle Bob who is probably the preeminent object-oriented guru and in this talk he presents a program that parses command-line arguments into a more convenient data structure such that say you can query hey what is the value of the flag that starts with - B or - C some - some character right and these arguments are specified with a schema string in this format here they're separated by commas and if it's a boolean it's not marked by any special character but if it's an integer it's marked with a number sign and if it's a string that is marked with an asterisk and so here for example this schema string expects an in flag J abullah flag F and a string flag W so given a schema if say we then had actual command-line arguments of - J 35 and then - W blah blah blah then the fact J will have the value 35 the flag F will have the value false because there was no F flag and then flag W will have the value blah blah blah so first he presents a version of his code which he thinks is not so great but then he cleans it up and this is that version I will I won't bother showing the bad version it didn't seem any worse or better than the this better version this ostensibly better version so I'll just show the better version so what does he have here first he has this abstract argument Marshall or class with two abstract methods set and get I don't know why he's using an abstract class rather than an interface because there's no you know there's no actual fields here so why live in abstract class and not interface whatever that then gets extended into three concrete classes there's boolean argument Marshall err there's also then string argument Marshall err and integer argument Marshall er and their bodies are really similar they're doing the same stuff except they're dealing with boolean string and integer values and that's the main difference there and then those three concrete classes are utilized in this args class which is where he has his main so I'm not going to step through any of the code so just look at the bottom right where we have main and so we instantiate args we pass in a schema string and then also the actual arguments to the main function that gets us back this args data structure which we can inquiry with methods get boolean get int and get string and so here we're asking hey what is the value of the boolean flag l and we get back a boolean value and so forth and then so we parse all of our arguments and get their values and then we pass them off to this execute application method which he doesn't actually it's not implement it's just hypothetically here's your actual program start and notice that the Argos instantiation and let's get boolean get int and get string methods are all called inside this try block because they can all throw args exceptions now if you want take a few moments in studies code and try and figure out what's going on he does a few really odd things because all he really needs to do is this there's my solution I just have a single class args with the same main function down at the bottom will show on the next slide but first args has three fields it has a hash map four characters two integers a hash map four character strings and hash map four characters two boolean and then in the constructor we pass in just like his args we pass in the schema string and the actual arg strings and first what we do is we parse the schema in this loop we we split the schema string by comma and then for each element of the schema we add that character to the appropriate hash map given the the symbol character that might accompany it and for now we're just giving the integer and string and boolean values in these hash maps default values for boolean the default value is false but then for string and integer the default value is null because it makes sense for boolean flag if you omit the flag then presumably then it should be false but if you omit a string or integer flag and who knows what that should be so there's not really a default value there in any case our arcs constructor then continues and we loop through all the actual argument strings and we check first if they begin with a hyphen because if they don't begin with a - we don't care about them we just ignore them but if we do then we check the next letter and we see well is that in the boolean map or the strings map or the in SMAP and whichever one it's in we want to take the specified value and assign it to that character in the hash map understand in the default case here in the else clause if we encounter a character which we didn't see in the schema then that's an sumption you're not supposed to we're not supposed to have any such flags if they weren't in the schema so that constructor gets us our fully parsed data structure and then just use a thing we have the same methods that he has in his code we have get bool getstring and getint and the main function here is exactly what he had but actually what I would do is probably I wouldn't have get bool getstring and getint I would just make the hash map fields public and then access them directly and I prefer explosive error handling over exceptions so there was only go to looks something like this we get args in stock get but that might be Knoll and if so that we that's an error otherwise if it's not null then we can get as value and yeah this is a little ugly in verbose but that's mainly just because Java has obnoxious distinction between primitives and and object types so that's really the root problem here so yeah this is a little verbose being explicit about errors but I still think it's better to be explicit so let's step back and look at the differences here and my version the code we had an Argus data type it had its constructor and there was the main method and that's the entire surface level of the code if you want to understand what's going to my code no question whatsoever where you need to start in Uncle Bob's code we have not just one class we have five one of those abstract which itself is mystifying in its own special way it has this strange notion of marshallers I still can't figure out what that's really supposed to mean and what's oddest of all is that these Marshall or classes are all recursive boolean argument Marshall has a boolean value field but then also it's boolean arcs field is a map of characters to other argument marshallers I don't know why he made it the the parent class argument Marshall or because it turns out in practice it's always specifically a boolean argument Marshall so I don't know what's going on there but I don't know why it's recursive to begin with it it's very very strange I think what happened is that because he was programming in an object-oriented style he just didn't have the clarity to see how simple what he was doing really was and so he couldn't then devise a sensibly simple solution when someone comes to his code and they want to make heads or tails of it they have to ponder why he has these five different data types and what purposes they really serve and then looking at the fields why are these data structures recursive and then you have to look at all the methods and everything split up into tiny little methods so yes in the straightforward procedural version there's a little bit of complex logic as you can see in the loop and branching but his version just chops that up and obscures it it scatters it all to the winds if you think simply in terms of data and data transformation rather than trying to conceptualize your data as doers and your your actions as somehow also being data types if you avoid that confusion it's much much easier to arrive at straightforward solutions like I did where I just have this hours class with the three hash maps because what was the task at hand I had this array of strings and I just wanted to convert it into a form of data that was more convenient to query just a form of data that was more convenient to use down the line that's all I was doing by avoiding all of his object-oriented nonsense we can have that clarity this brings to mind another talk by sending that's where she explicitly concedes that object-oriented code is generally less comprehensible than procedural code but she defends object-oriented code with the idea that you're not supposed to understand the entirety of your code if you properly do compose everything into these the system of little objects then you can make changes to your code by just adding in more objects without having to fully understand all the connections between them and all the interactions aside remain skeptical that this is actually true that you actually get that benefit and entren your code that you can proceed without really understanding your code I think this is fundamentally just the wrong way to program yes you do need to understand your code and beyond that you need to understand your tools and you need to understand your environment meaning generally your platform when we give up on understanding what we're actually doing when you write code it just leads to bad software
Info
Channel: Brian Will
Views: 1,169,684
Rating: 3.4035203 out of 5
Keywords:
Id: IRTfhkiAqPw
Channel Id: undefined
Length: 28min 2sec (1682 seconds)
Published: Fri Mar 04 2016
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.