10 ms·
Object-Oriented Programming is Garbage: 3800 SLOC example [video]
- chriswarbo 11y agoI enjoyed this. Skipped through the parts about the inner NES workings, but the "embarrassing" video is more to the point and shorter. I found myself agreeing with a lot of points and disagreeing with some too. It's interesting how many comments there are, here and on YouTube, along the lines of the examples being "bad code, regardless of OOP", "not representative of a large team/project", "unmaintainable", etc. without even a link to an example snippet or repo, let alone a narrated walkthrough of existing code and possible alternatives like the author has provided. Without concrete examples, all of these counter-arguments are in danger of a No True Scotsman fallacy. In fact, the code in the "embarrassing" video are actual examples of respected OO programmers showing how OOP should be done, which is a high bar to refute. The author also addresses this a little, by pointing out the often-invoked circular reasoning of "my software's a mess even though I followed OOP", "if it's a mess you must not have been doing OOP properly".
- Chris2048 11y agoSometimes software methodologies are a little like the stone-soup story; vaguely defined such that when it turns out bad, you can always wriggle out of it by claiming it was done wrong, and there's a better way to do it that works. When you follow up for details, the full "right way" has such a high bar, no one but a few true believers are gonna actually follow it. I want a methodology that provides improvement on average, even with only partial uptake. Many other best-practises are independently virtuous, hence have this quality.
- throwywowy 11y ago> youtube
- Nr7 11y agoI don't like the clickbaity title. OOP != garbage, badly done and/or unnecessary OOP == garbage.
- sklogic 11y agoGiven that it is unnecessary in 99.99% of the cases, it is always safe to assume that it is just a garbage, period.
- charlesism 11y agoIt's more a question of degree. It's pretty rare to find code these days that is completely OOP free, aside from machine code, so I'd argue "99.99% unnecessary" is hyperbole.
- sklogic 11y agoBut still, it is completely, totally unnecessary everywhere it is used. I only know one problem domain where a form of an OOP is required (although it must be built on top of asynchronous messages, i.e., none of the existing popular OO implementations would fit) - agent-based simulations. It is well below the 0.01% threshold I set so generously.
- charlesism 11y agoForgive me, but "totally unnecessary everywhere" just sounds like religion to me. Are structs unnecessary? It's an awfully short walk (one pointer long) from a struct to an object.
- sklogic 11y agoOk. Mind naming a single problem domain besides one I've mentioned that should be modelled the OOD way? There are none. And please do not confuse the OOPish language features (which are just poor man modules support in most cases) with OOP-the-design-methodology. The latter is garbage, the former is just an innocent bunch of language features. Btw., it is funny when OO zealots are calling anything else a "religion". They should look in a mirror first. OO is a religion and nothing but a religion.
- 11y ago
- thunderbong 11y agoPrevious videos Object-Oriented Programming is Bad https://www.youtube.com/watch?v=QM1iUe6IofM https://www.youtube.com/watch?v=QM1iUe6IofM Object-Oriented Programming is Embarrassing: 4 Short Examples https://www.youtube.com/watch?v=IRTfhkiAqPw https://www.youtube.com/watch?v=IRTfhkiAqPw I'm watching the 'is Bad' one, and honestly, it rings too close to my experience. Need to think about this a bit more deeply!
- bitJericho 11y agoGoing from 10 years of strictly procedural programming to oop I can say with certainty that for most applications oop is better hands down.
- mateuszf 11y agoThe other alternative is functional programming (which is usually not the same as procedural programming).
- kaeluka 11y agoI think that FP is a valuable addition, but not strictly better than OOP (or procedural/imperative). I think that some problems are better expressed in FP, and some are better expressed in an imperative manner and a language should let you choose. edit: s/think/BELIEVE/
- pdkl95 11y agoThat conflict between OOP and FP is the "Expression Problem". Choosing which side to use for a given problem is an important part of software design. http://c2.com/cgi/wiki?ExpressionProblem http://c2.com/cgi/wiki?ExpressionProblem
- kaeluka 11y agoWhat you say is sound but, IMO, not complete. There's something more to immutability vs. mutability. They both can, for instance, describe both data bases and mathematics. But there's no doubt in my mind that mutable state is better suited for the former, and immutability for the latter. It's "stuff" vs. "knowledge" that's modelled; stuff wants to change and knowledge wants to be shared.
- insulanian 11y agoCan someone provide TL;DR? Can't access YouTube from office :(
- viraptor 11y ago3'rd video in a series, comparing more and more complex pieces of code, trying to prove OOP is unnecessary in most real-world cases. Worth watching whether you agree with it or not - it can't be really TL;DR'd.
- spuz 11y agoRead the initial article that spawned this series of videos here: https://medium.com/@brianwill/object-oriented-programming-a-personal-disaster-1b044c2383ab https://medium.com/@brianwill/object-oriented-programming-a-...
- mpweiher 11y ago"Sure 90% of OOP is crap. That's because 90% of everything is crap" [1] [1] https://en.wikipedia.org/wiki/Sturgeon%27s_law https://en.wikipedia.org/wiki/Sturgeon%27s_law
- deleted 11y ago[deleted]
- deleted 11y ago[deleted]
- calibraxis 11y agoHaving just dealt with it again, I'm tempted to grumble that Java's OOP is firmly within that 90%. What happens when something is recursively buried in 90% crap? Is its own 90% particularly crappy?
- raould42 11y agoyup, i have always maintained it is a recursive/fractal/recurrence thing where 90% of the remaining 10% is crap, ad nauseum. crap all the way down.
- ArkyBeagle 11y agoIt's more like the limit converges on some x in 0..1 with 90% being the most probable.
- charlesism 11y agoIt was 3800 lines of code before, and it's 3800 lines of code after. Let's spend the remaining 45 minutes discussing why my personal preferences are correct, and OOP is garbage.
- mpweiher 11y agoThat was my thought as well. In order to justify the crass title, something a little more than a circular argument as evidence might have been called for.
- charlesism 11y agoYes. Plus, it's somewhat arrogant to deride the legibility of someone else's code, in comparison to your own rewrite, considering: (a) It's an order of magnitude more difficult to write original code than to port code from one domain to another. (b) It's an order of magnitude more difficult to understand someone else's code than something you've just finished writing. It would be a different story had he had created the rewrite, and had a third party discuss both versions.
- loup-vaillant 11y agoThe procedural version have less files, and less functions/methods. This indicates that many functions in the original code were called only once, so we might ask why they were broken up in the first place. Having that many functions, that are called only once, means you have to make more non-local jumps to read said code. This is not ideal. Moreover, such a break-up obscures the dependency graph in the code. Many nodes (functions) will have only one inbound edge (call). While collapsing the original graph to a canonical form will likely yield the same result on both code bases, the original code still show 3 times as many nodes. This makes it harder to see the real dependencies. Again, not ideal. Finally, there's the possibility that the procedural version made genuine simplifications to the dependency graph. If it really reduced coupling while keeping the LoC count the same, that's a net win. Granted, those measure are not nearly as definitive as the LoC count itself. Still, they do suggest the original code is not as straightforward as it could be. Now to really know, we'd have to read the code.
- raverbashing 11y agoOO in itself is not bad But of course some people want to create 10 level class hierarchies, replace every 'if' with a subtype and using design patterns ate every occasion (especially the factory, so instead of writing 1 or 2 they will have a OneIntegerFactory, a TwoIntegerFactory, etc) One advantage into Go for example is that it reduces the potential of misuse of "OO happy" people
- notinreallife 11y agoMy guess is that most "OOP-happy" people never get a chance to see beautiful procedural code so they're just afraid of it. A lot of developers nowadays are trained to smite any code that isn't following OOP principles or design.
- nibnib 11y agoNot necessarily afraid, possibly just unfamiliar. I can remember programming like this because C++ was the first language I knew well and I was interested in using all the available features. In my mind that was just how things were done. When I had more of an incentive to just Get Stuff Done and keep things maintainable simpler procedural code made a lot more sense.
- deleted 11y ago[deleted]
- pdkl95 11y agoWhile I'm sure the topic (and title) may be seen as hostile, this is a very nice code walkthrough and discussion. The arguments against OOP seem to be 1) OOP obscures data structures by hiding them in too many data types, and 2) the boundaries created by these data types forces workarounds like extra fields to hold a root structure pointer even though it isn't part of the type. OOP - in practice - does tend to encourage far too many types. A lot of these types are often represent code or design decisions, not the actual data. Instead of a "few" structs that clearly show how the data is organized, we tend to see OOP code that has the data structure spread out over various classes, interleaved with types that actually represent different actions (with the same data). The solution I always try to keep in mind is that OOP can be useful if-and-only-if the data actually has a hierarchy. A single standalone class is equivalent to a basic struct passed to functions. The class only becomes worthwhile when are able to use polymorphism that closely model the data. The serious problems start when you try to force everything into a class; in extreme cases you end up with the Kingdom of Nouns[1] The second argument seems to follow from the first; if you have too many types, you tend to take shortcuts like keeping a local reference to a parent or global object that is really outside your scope. This tight coupling creates maintenance problems and further confuses what the type is actually representing. [1] http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom...
- sz4kerto 11y ago> A single standalone class is equivalent to a basic struct passed to functions. Not exactly -- a struct is equivalent to a class with public fields. If you have a non-anemic model class then you can guarantee that the object's state is within some boundaries (defined in the mutator methods).
- stcredzero 11y agoThe solution I always try to keep in mind is that OOP can be useful if-and-only-if the data actually has a hierarchy...The class only becomes worthwhile when are able to use polymorphism that closely model the data. You don't need a hierarchy to have polymorphism. The Observer pattern is very useful in my space game in Go. I wrote a small pair of Interfaces which makes it very easy for certain objects to register to receive notification when a ship despawns. type DespawnListener interface { NotifyDespawnShip(Ship) } type DespawnNotifier interface { SetDespawner(Ship) AddDespawnListener(DespawnListener) NotifyDespawn() } type basicDespawnNotifier struct { ship Ship dependents map[DespawnListener]bool } func (self *basicDespawnNotifier) SetDespawner(ship Ship) { self.ship = ship } func (self *basicDespawnNotifier) AddDespawnListener(dsl DespawnListener) { self.dependents[dsl] = true } func (self *basicDespawnNotifier) NotifyDespawn() { for key, _ := range self.dependents { key.NotifyDespawnShip(self.ship) } } My framework sends NotifyDespawn() to any despawning ship. Now anything that has registered interest in that ship, like missile locks, tracking AI ships, or tracking missiles, will be notified when that ship despawns. Note that this makes the system extensible. Right now I have each Ship entity tracking dependents in its own map, which it can do by composing a basicDespawnNotifier into its struct. Later, I will move the implementation guts into the object that implements the game loop/instance, which will eliminate the map from despawning entities, reducing GC pressure, and allowing me to cut-off circular dependencies. Note that all of the game code using the two interfaces won't change at all. No hierarchy is necessary in Golang or Smalltalk, because both have implicit interfaces and allow composition. (Golang is nice because its implicit Interfaces are made explicit and typesafe.) The op is somewhat misguided. Good OO is comprised of good design, just as good computer games are comprised of good design. Expecting good OO of any programmer who just knows the "rules" of OO is like expecting the output of all game jam hackathons to be good games. Of course half of all OO projects found on Github with "obligatory" OO are going to show below average design, just as not every game from a game jam is not necessarily a masterpiece.
- enqk 11y agoMethodology wise this approach is problematic, because as a refactorer he gets to stand on the design discoveries made by the original programmer.
- dang 11y agoSpot on, but this is an insight whose time hasn't yet come. For one thing, it means that controlled comparison of programming approaches is even more impossible than we already know it to be, which is a bummer, which is a reason not to have the insight. What the OP did is still a worthwhile exercise. And although it seems to have been an accident, he made a good choice of program to study in this fashion. Because it is an emulator, what it needs to do, and therefore much of its design, was predetermined before the first program was written. That makes the comparison with the second program somewhat less vulnerable to your objection, though we have no way of measuring how much.
- enqk 11y agoGood insight, I actually was wondering if the choice of the object (Emulator) was a good or bad one. You're making a good case that it reduces the effect of initial design discovery since it's based on well known hardware.
- lyudmil 11y agoThis is a stimulating argument to think about. One possible counter-argument is that the resulting procedural code is more difficult to unit test. In the video Brian acknowledges that he doesn't think much about cyclomatic complexity, but I think if you were to try to test one of these long functions with a lot of branches, the setup required for each case you want to test would dominate the amount of code devoted to the actual test. The solution to this isn't necessarily OOP, but I would not want to maintain Brian's code as it stands without refactoring it. The more fundamental problem to me is that Brian's code requires you to read it fully in order to understand it. No part of the code is difficult to understand, but it demands that I understand it in detail before I can have a summary of it in my head. I prefer for code to read more like a newspaper article, where the further down you read, the more detail you're given, but you can stop at any point and have a decent understanding of what's going on. To me this makes it easier to reason about the system as a collection of sub-systems, which is often the level at which the really important decisions are made. Again, OOP isn't the only "solution" for this, but I think it's a good tool. It does have the potential, as Brian points out in one of his earlier videos, of devolving into philosophical exercises, but I don't think that's necessarily a drawback if your goal is to make it easier for humans to comprehend your system. In designing your system this way, you're simply taking advantage of people's natural ability to think in abstractions.
- piokuc 11y agoTo me, the idea of inlining functions that are otherwise called only once is complete garbage. Yes, you reduce number of functions in your program but the program doesn't get easier to read, test, develop and maintain as the functions that you leave get longer... I stopped watching when I learned that that's presenter's idea of improving the original program. And it has _nothing_ to do with OOP and its flaws, you could as well "improve" this way well written functional programs... It's just wrong. Inlining functions is something your compiler is supposed to do for the programmer, a programmer is better off breaking his problem into small functions instead.
- derrickdirge 11y agoI couldn't agree more. When I read your enormous procedures full of inlined functions, I still have to break it down into discrete, logical chunks in my brain. This is work, and there's a real chance I'll get it wrong. If you do it for me, I'm much more likely to understand your program in the way you intended.
- sz4kerto 11y ago3800 lines of code is an extremely small program. OO can help when you have a large business project, large team with people, some of whom are possibly not great coders. Showing that one single person can write a small procedural program that's more structured, faster, etc. than the corresponding OO code written by someone else is not very relevant for real-world projects. I'm not saying OO is great everywhere, but devs in the industry have been writing working code on large scale using OO.
- sklogic 11y agoWhat you're talking about is just better code organisation and modularity. It's a shame it is always brought up in defence of the useless OO, as if modularity is an exclusive OO property and not a mere side effect of it.
- bubuga 11y ago> as if modularity is an exclusive OO property and not a mere side effect of it. Modularity isn't a mere "site effect" of OO: it's one of the main motivation factors that have lead to OO. In fact, the main problem with these anti-OO rants made by procedural purists is that in essence they all boil down to claiming that basic OO features that are a part of any OO language can be reimplemented in procedural languages.
- sklogic 11y ago> it's one of the main motivation factors that have lead to OO. OO got no rights whatsoever to highjack the benefits of the modules. There are some very successful module systems that got absolutely nothing to do with any OO bullshit.
- bubuga 11y ago> OO got no rights whatsoever to highjack the benefits of the modules. You're the only one who's trying to make that assertion. It's a strawman, and a poor one at that.
- smarx007 11y agoI just want to note that original code wasn't covered with unit tests and the rewrite is not going to make it any more suitable for testing (as you expected, no tests were added during the rewrite). On the critical note, http://pythontesting.net/strategy/why-most-unit-testing-is-waste/ http://pythontesting.net/strategy/why-most-unit-testing-is-w... is a good complementary read.
- gelraen 11y agoGot through first 5 minutes and I already see that myself and the author have very different opinions on what constitutes "good code": 1) having data structure and its methods isolated in a module is not a bad thing. Modules provide an obvious boundary between pieces of code, so when calling some code from another module you know that you should care only about inputs and outputs and not about the particular implementation of the transformation. But when you have the same pieces of code mashed together in one module – that boundary is blurred, now compiler does not tell you that they don't interact via some shared state not named explicitly. 2) defining an interface to do a type switch. Really? Even if that interface is not exported, this still leads to more fragile code. When you rely on interface compiler will yell at you if you miss a method. But when you add a new type and forget to update one of those type switches – it will blow up in your face at run time. This is less relevant for NES emulator, since it was designed long ago and unlikely to get new entities that need to be accounted for, but doing this for no good reason can establish a habit that will bite you later in other projects.