21 ms·
Class Hierarchies? Don't Do That
- ssmoot 13y agoOw
- mercurial 13y agoIt's just as true in other languages. More often than not, programmers thinking about code reuse attempt to solve it via inheritance, which is almost always the wrong answer. With some persistence, you can end up with a nice four or five level deep inheritance tree with 10 methods randomly overriden at various levels in the tree. Good luck figuring out how the damn thing actually works if you didn't write it.
- dgcoffman 13y agoOkay, but that kind of polymorphism is actually a replacement for conditional logic, e.g. switch on type antipattern. People hate conditionals. People hate polymorphism. Everybody is wrong about everything pretty much all of the time, it appears.
- cmbaus 13y agoIn JavaScript, like other duck typed languages, you have dynamic dispatch without inheritance. For example: var objA = {doIt:function(){console.log('hello from a')}}; var objB = {doIt:function(){console.log('hello from b')}}; var doItDynamically = function(doitObj) { doitObj.doIt(); }; doItDynamically(objA); doItDynamically(objB); a and b do not share a common class (since classes do not exist in JavaScript), but they implement the same interface. For this reason, they can be used polymorphically, as if they shared a common base class or interface in Java or C++.
- deleted 13y ago[deleted]
- mercurial 13y agoYou can usually get code reuse via composition. If need be, you can have your objects implement one or more common interfaces (which may themselves inherit from other interfaces, that's not a problem).
- MartinCron 13y agoEverybody is wrong about everything pretty much all of the time, it appears. Misanthropy Driven Development? I would like it if I didn't already hate everyone and everything.
- aturek 13y agotl;dr: Superclasses make your code brittle. https://en.wikipedia.org/wiki/Fragile_base_class https://en.wikipedia.org/wiki/Fragile_base_class How many people still try to fit their software design into the class hierarchy model? I've been on the composition-not-inheritance side for so long I can't do "traditional OO" justice, but I'd love to hear the counter-arguments in case I'm wrong.
- jdlshore 13y agoNo, you're right. "Favor composition over inheritance" has been the go-to OOP design advice for years. The distinguishing feature of OO is modeling concepts by encapsulating state and behavior. For some reason, introductions tend to focus on complex inheritance structures instead. I think that's really unfortunate because it sends entirely the wrong message about OOP. That said, I do occasionally use inheritance for polymorphism and limited code reuse (such as [1]), but I keep it limited in scope, and I don't think I've ever used a multi-level inheritance hierarchy. [1] In my Let's Play TDD screencast series, I use inheritance when modeling a "Dollars" value object. There's a Dollars base class and then various specializations: ValidDollars, InvalidDollars, and UserEnteredDollars. Source code: https://github.com/jamesshore/lets_play_tdd/tree/master/src/com/jamesshore/finances/values https://github.com/jamesshore/lets_play_tdd/tree/master/src/... The screencast: http://www.jamesshore.com/Blog/Lets-Play/ http://www.jamesshore.com/Blog/Lets-Play/
- mpweiher 13y agoAny dependency makes your code brittle. On the other hand, you can't reuse without dependencies. Power/safety tradeoff. Superclasses are just a special case of this.
- stonemetal 13y agoMore like Superclasses used incorrectly make your code brittle so don't use superclasses. In so far as I have never found a use case for a class hierarchy 5 deep I agree with him. On the other hand the argument that because you can do it wrong means you shouldn't do it all isn't compelling. I don't think there is a language or language feature that you can't use incorrectly.
- _getify 13y agoHuge fan of this article. Makes some really great points! Wish I could share and vote it up a thousand times. :) I have been writing about exactly this topic, of why classes don't make sense (specifically for JS), in my second title of the "You Don't Know JS" book series, "this & Object Prototypes". In particular, Chapter 6 makes the case for an alternate pattern to class-orientation design, which I call OLOO (objects-linked-to-other-objects) style code. OLOO implements the "behavior delegation" design pattern, and embraces simply peer objects linked together, instead of the awkwardness of parent-child abstract class semantics. https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch6.md https://github.com/getify/You-Dont-Know-JS/blob/master/this%...
- tasty_freeze 13y agoKyle, the figures (fig4.png, fig5.png) are missing from that page. Other pages too. I'm one of your kickstarter contributors, but I was unable to read & review some of the chapters because significant parts of the text refer to missing diagrams.
- _getify 13y agoThey're coming. It's an artifact of working with a book publisher and the art department and all that. Sorry for the confusion.
- tasty_freeze 13y agoA rough, hand drawn sketch scanned to gif or png until the official artwork is ready would be 100x better than a broken link.
- _getify 13y agoOK, draft figures posted now. https://github.com/getify/You-Dont-Know-JS/tree/master/this%20%26%20object%20prototypes https://github.com/getify/You-Dont-Know-JS/tree/master/this%...
- 13y ago
- cmbaus 13y agoI am bit concerned that the standard committee is going to make the language worse while trying to fix it. Yes it is inconvenient to do traditional OO programming with JavaScript, but I'm not convinced that is a bad thing. Encouraging subclassing, as pointed out by the author, could actually be detrimental.
- camus2 13y agoI am bit concerned that the standard committee is going to make the language worse while trying to fix it. AFAIK the goal is write what you mean and not "fake stuffs because the language lacks structure". Dont want to use any ES6 new syntax, dont use it. And yes , the committee SHOULD meet developpers demands as much as possible. Javascript has no clear paradigm,everything should be on the table.
- cmbaus 13y agoTo clarify, what concerns me is that by adding syntax that is similar to Java, developers coming from other languages (namely Java), won't take the time to understand the differences between the two languages and continue to write applications as they would in Java. Everyone has their biases, and my bias would be to embrace the dynamic and functional aspects of the languages that separate it from Java, rather than creating new syntax that pastes over the fact that the language is fundamentally different.
- peterashford 13y agoI'm a Java programmer and I think classes in JS would be awful. Tacking on the ES6 behaviour will not make JS a sane OO language, it'll make JS a bunch of new, inconsistent behaviours tacked onto the old, inconsistent behaviours. FWIW I'm with you: I think that treating JS as a functional language is a cleaner fit that crowbar-ing OOP into it.
- V-2 13y agoYou don't like TypeScript?
- hibikir 13y agoInheritance is easily overused, but that doesn't mean that we should just avoid it altogether. The problem IMO is that we are stuck in a view that inheritance is really about ontology, when what we really mean, and want, is code reuse. It's very hard to make a 5 level deep ontology not break down. This is why we have this whole 'prefer composition over inheritance' business. But while we are stuck with that kind of mindset in Java (at least pre java 8), we miss the capability of using inheritance as a way of adding mixins. There is much power in using mixins as a way to clarify composition, while we still keep the inheritance tree very shallow. That's one thing we get with judicious use of the Scala cake pattern, which you could easily reproduce in javascript: Composition without really having to write a bunch of useless boilerplate. There's a nice talk out there about it. Cake Pattern: The Bakery from the Black Lagoon. The trick, as with most other programming techniques, is to use it carefully.
- kenrikm 13y agoI'm coming at this from the view of an Objective C programmer, which was heavily influenced by smalltalk. Having written Objective C full-time for 5+ years, it is rare to need to use inheritance in classes that actually implement application level logic, however it's used in just about every class you make as you inherit from Apple's base. (UIViewController, UIView, etc..) It's useful to subclass and add categories as needed. At a functional level it's not an issue, however with that in mind I'm not sure it's a great pattern for Javascript.
- chadhietala 13y agoNot trying to be tangental, but I believe this is true with EmberJS which was originally modeled off of Coaco. You inherit from Ember's base Objects (ArrayController, ObjectController, etc) and rarely find yourself extend classes that you write. Here is a hierarchy of the "base" classes. http://emberjs.jsbin.com/bahetoka/1 http://emberjs.jsbin.com/bahetoka/1
- sunir 13y ago"what we really mean, and want, is code reuse" I'm not sure if you meant it this way, so I'll underscore this point. Code reuse is truly the worst "reason" to use inheritance, and in fact an anti-pattern. Composition is the only proper way to reuse code because composition is explicitly stating you are using the composed code.
- nabla9 13y agoWe want to use classes because we want to want to be able to invoke an operation and have the exact behavior determined by the type of the object or objects on which the operation was invoked. Common Lisp philosophy of classes and OO fits the thicketness of the real world better. Generic functions, multimethods, mixings etc. Just embracing the notion that classes and encapsulation might be orthogonal issue opens up the way to use class system better ways.
- DrJokepu 13y agoIt's difficult to argue with this article. I can count on one hand the number of times non-trivial class hierarchies (that is, more more than 2-3 classes) made my life easer and every time that was the case I writing a compiler or a code generator or something similar.
- mpweiher 13y agoThe Magnitude/Number hierarchy in Smalltalk is amazing.
- kenjackson 13y agoGood article, but I think the real message isn't to not use inheritance, but to use with great care. Inheritance gives you a great characteristic -- isa relationships. And this is something you don't get with composition. That said, the fragility with poorly constructed base classes is real. But a succinct base class can be very valuable and useful, and not that brittle. Just don't stuff cruft in it that is of questionable value. Ask yourself what's the least you can put in the base class and still provide value. And this is where interfaces are also useful. You can get the isa relationship w/o much of the brittleness as there is no internal state associated with the interface. But you are still creating a hierarchy (just not of classes, but of interfaces). It's a useful article, especially for those new to the idea. But the takeaway should be to use care. Not to avoid at all costs.
- tomphoolery 13y ago> But the takeaway should be to use care. Not to avoid at all costs. I think the takeaway here is to not try to use JavaScript as if it was some other language that has support for classical inheritance, but rather to embrace the prototypal inheritance model and compose your apps in a different way. It's not a "GOTOs considered harmful"-style rage post, but rather a warning to budding JS developers who are coming from a more classical perspective. It's not that you "shouldn't use classes", it's that you shouldn't build complex hierarchies and type systems. Keep it simple!
- ams6110 13y agohttp://javascript.crockford.com/prototypal.html http://javascript.crockford.com/prototypal.html
- pdonis 13y agoYou can get the isa relationship w/o much of the brittleness as there is no internal state associated with the interface. You can even get the relationship without brittleness but with some internal state, as long as the superclass wraps the internal state in a property. For example, in Python, the currentBalance private state would obviously be implemented as a property, not an instance variable, so that it could be mutated by subclasses while still hiding the private implementation in the superclass. (This would be harder to do in JS, I admit, since JS doesn't have anything corresponding to Python's properties.) So the lesson here isn't really "don't use class hierarchies", it's "don't use class hierarchies stupidly".
- porker 13y ago> It turns out that our knowledge of the behaviour of non-trivial domains (like zoology or banking) does not classify into a nice tree, it forms a directed acyclic graph. Or if we are to stay in the metaphor, it’s a thicket. That completely chimes with my experience. I wondered if I was doing OOP wrong, as any time the size/complexity of a project (or module) gets above a certain level, a Thicket results in my code. > Classes are the wrong semantic model, and the wisdom of fifty years of experience with them is that there are better ways to compose programs. Where's your source/links? What? This needs expanding - if there are better ways, outline your evidence and show us where we're going wrong :)
- skybrian 13y agoThis is generally true. On the other hand, interfaces are a good thing, especially in a language like Go where you can explicitly declare them and then pass in anything that happens to match that interface. (In JavaScript, this is unfortunately implicit, so it requires better documentation and sometimes runtime checks to ensure sanity.) If you implement an interface using delegation, you get something quite like subclassing, except that the subclass-superclass interface is explicit and the superclass's type is entirely hidden. Sadly, few languages make this easy so it often requires some boilerplate or meta-programming.
- cromwellian 13y agoDelegation can have a performance cost that inheritance doesn't have however, at least, depending on the compiler. Go's current compiler seems engineered for correctness, not performance, at least last time I checked, the JVM was beating it on several benchmarks.
- cmbaus 13y agoDepending on the language, delegation can be faster. For instance in C++, virtual function dispatch is slower than calling a non-virtual member. It is easier for the compiler to optimize member function calls vs virtual functions which cannot be inlined.
- cromwellian 13y agoIf the compiler does global optimizations, it can infer when virtual functions aren't really virtual. For example, if a virtual function is never overridden, or never overridden by any live type (after dead code removal), then it can be implicitly converted to a non-virtual function. Since C++ doesn't have interfaces, only pure virtual functions, you'll pay the cost of the virtual dispatch, even if you use delegation, won't you? (The context of my comment is the use of interfaces, with implementations using delegation instead to do implementation inheritance) e.g. interface I class Concrete implements I delegate to Shared class Concrete2 implements I delegate to Shared Yes, if you don't use any interfaces at all, and just a concrete class, then it's a different situation.
- dclowd9901 13y agoEver since I've started writing Objective-C, a lot of Javascript's glaring issues have started to melt away. Yes, JS doesn't have an understanding of protocol, but that's just a matter of implementing convention and being disciplined. JS is remarkable in that it is so malleable that one can use myriad patterns with it. I've found that the delegate pattern, for instance, can be incredibly powerful when used in conjunction with JS, especially when it comes to extending classes functionality that may not be inherent to its topology (think class Ant, to describe an Ant, and class FlyingAnimal -- wings, etc. --, to describe a queen ant).
- epx 13y agoCocoa is good because, among other things, is treads lightly on class hiearchies.
- jdlshore 13y agoOkay, having read the article, I think it goes too far. Inheritance hierarchies have their issues, and raganwald touches on them, but there's a strawman argument here. (Incidentally, raganwald, I've noticed this about all your OO articles. You seem to have a bias against class-based design. It's causing your essays to be less brilliant than they could be.) Fundamentally, you can think of inheritance as a special case of composition. It's composition combined with automatic delegation. In other words, if you have A with method foo() and B with method bar(), "A extends B" is equivalent [1] to "A encapsulates an instance of B and exposes 'function bar() { return this._b.bar(); }'." This is very useful when you want polymorphism. Writing those delegators is a pain in the butt. More importantly, it tells us how to use inheritance safely. Only use inheritance when 1) you want to automatically expose all superclass methods, and 2) don't access superclass variables. Now, JavaScript does have the specific problem that you can accidentally overwrite your superclass's variables, and that's worth talking about. But I think that saying "inheritance is bad" goes too far. The article would be stronger if talked about when inheritance is genuinely useful, the problems it causes, and how to avoid them. Edit: In particular, I want to see more about polymorphism. Polymorphism is OOP's secret superpower. Edit 2: I'm not saying polymorphism requires inheritance. [1] Not quite equivalent.
- glenjamin 13y agoPolymorphism can be acheived without using inheritance. See Clojure's Protocols or Haskell's type classes for examples of this.
- oinksoft 13y agoSomebody has also made a JavaScript library for this: https://github.com/Gozala/protocol https://github.com/Gozala/protocol
- dllthomas 13y agoTwo points here regarding Haskell. First, a function with typeclass constraints is less polymorphic (in that it will operate on fewer types) than polymorphic function without typeclass constraints. Of course, [the fully polymorphic version is] also more limited in how it interacts with the corresponding values. Second, parametric polymorphism in Haskell is statically resolved. You can have polymorphic functions, but any given container still contains a single type. You can still do dynamic polymorphism in Haskell (by storing a list of records of functions, rather than storing data directly) but it doesn't typically involve type classes.
- al2o3cr 13y agoAnybody have an original source for "prefer composition over inheritance"? I've heard it for years, but never with an attribution. FWIW, I noticed that in my old C++ book (Stroustrup '91) there's some really bogus inheritance examples - window -> window_w_banner -> window_w_menu -> window_w_banner_and_menu etc etc etc.
- djur 13y agoThe idea is presumably older, but I think the current usage can almost always be traced back to _Design Patterns_ (GoF, 1995). It's in the Introduction. I don't think Stroustrup really gets OO. I have yet to read a book of his that doesn't have some absolute howlers.
- SideburnsOfDoom 13y ago> Anybody have an original source for "prefer composition over inheritance" It is mentioned in the GoF Patterns book[1], from 1994/1995. that's what I think of when I hear the phrase. 1: http://en.wikipedia.org/wiki/Design_Patterns http://en.wikipedia.org/wiki/Design_Patterns
- cwp 13y agoThe substance of the article is spot on, but I have to take issue with the terminology it uses. The problem he's talking about isn't classes, it's inheritance. This is an important distinction. Javascript doesn't have classes, but it does have inheritance. The problems raganwald points out with the Account example come not from the organization of the code into pseudo-classes, but from the fact that it uses Javascript's inheritance mechanism. It would be perfectly possible to write a version of the example that exhibited the "fragile prototype problem," and conversely, one can easily write a version in Smalltalk that uses composition instead of inheritance and thus has no fragile base classes.
- hifier 13y agoThere are some contradictions here. For example the author the following statement about encapsulation violations being permitted, but not followed as best practice: "JavaScript does not enforce private state, but it’s easy to write well-encapsulated programs: simply avoid having one object directly manipulate another object’s properties. Forty years after Smalltalk was invented, this is a well-understood principle." However, the author doesn't seem to really understand this as he makes the case that access to private state and behavior of a "superclass" violates encapsulation: "In JavaScript (and other languages in the same family), classes and subclasses share access to the object’s private properties. It is not possible to change an implementation detail for Account without carefully checking every single subclass and the code depending on those subclasses to see if our internal, “private” change will break them." Well, yes they do allow access, but that doesn't mean you have to use it! This is considered bad practice when extending any class in other languages that I'm familiar with (C++, Ruby). Please take some of your own advice. I do agree that hierarchies do not fit the real world as well the contrived examples from my first OOP classes and they should be used with extreme caution. Let's not throw out the baby with the bath water, however.
- menzoic 13y agoChequingAccount.prototype.process = function (cheque) { this.setBalance(this.getBalance() - cheque.amount()); return this; } ...
- buzzybee 13y agoThe most challenging aspect of inheritance modelling is to recognize it on its intrinsics - the deliberate creation of coupled properties and behaviors associated with the "isa" declaration - and not simply as a form of taxonomy. Programmers are still inclined to use hierarchies. They are implicit to source code in a more general sense, with indentation, nested logic, etc. But we also need affordances to break up hierarchies and keep them limited.
- kwamenum86 13y agoI used to find these types of articles useful. But now I see them more as dogma. Great engineers don't struggle with things like JavaScript inheritance because they understand best practices and trade offs. So in general, I find it more useful to read about best practices and trade offs than "xyz considered harmful" articles that don't present viable alternatives to xyz. That said whenever I see something on raganwald.com I'll still read it :)
- jonahx 13y agohttps://yourlogicalfallacyis.com/middle-ground https://yourlogicalfallacyis.com/middle-ground The alternative he presented is not to share private state between class and superclass. This really isn't a "know the right tool for the right situation" kind of thing -- it's almost never a good thing. You could accomplish this alternative, among other ways, by: 1. Using composition and delegation. 2. Using mixins, if your language supports them. 3. Using inheritance, but depending only upon your superclass's public interface.
- sktrdie 13y agoPersonally I believe that software design is a highly subjective art. People that are meant to maintain a piece of code are different. Some may be more creative and less pragmatic, others may be more structured and clean in their design. The truth is that there's no silver bullet. You can build software based on hierarchy of classes because your mind works better with that structure, but others may find it completely inappropriate. We're human and our mind works differently from one another. In that sense I truly believe software is much closer to art than engineering.
- graycat 13y agoAnother battle in the 60 year language wars based on criteria that are so obscure that have battles just over the darned criteria.
- tmsh 13y agoClass hierarchies are easily fragile, easily do not model things correctly over time. The cruft, the lack of DRY as the mapping between what is being modeled and what is represented in the code -- accumulates with class hierarchies for sure. But the idea of inheritance and tree structures has been around and strong for the past 50 years -- not because people are inherently lazy -- but because it models something very primitive about programming: the evolutionary nature of development and thinking. This is extremely non-mathematical, but extremely non-trivial. Tree structures (and by extension 'hierarchies') are perhaps the most fundamental way to organize data. There is nothing more sophisticated than a tree search -- it is the basis of all exponential / efficient access times and all knowledge and organization of memory. It's why evolution proceeds in tree structures. We organize data in our mind in tree structures. Life over millennia organizes itself in tree structures. I'm sad to report that the world will not quickly become clear and abstract and orthogonal a la Haskell or other pure languages. Knowledge, life, everything proceeds via evolution, not something a priori. The sooner one really accepts that, the sooner one has new ways to interface with this reality more practically and effectively. People sometimes make the point of use the right tool for the job - as a reason for still using OOP, etc. I'd not say that -- but that the right tool for the job where the job is an evolving code base is actually something object-oriented with inheritance. Except where the domain is very precise and known ahead of time. Otherwise, the manner in which it models evolution is actually quite useful (despite the fragility of inheritance and the quick ability to spin out of any orthogonal clarity). People who program purely in Clojure or Haskell put the burden of this evolution in their own development as a programmer (there is no extension, there is just clarity and then rewrite). That's ok. People who use Java or whatever in the enterprise because it's easier for other people to get on board with it -- put the burden in the code. But the cost of modeling a problem that evolves goes somewhere.
- thomasahle 13y agoAre you saying that `nature uses is-a hierarchies, hence it's good for source too.' or that `source code that models real world concepts so it should follow the same concepts.'?
- tmsh 13y ago
- malandrew 13y agoI like these articles, but I think there are more than enough of them out there and not enough of those that show you how to use composition. Everyone who has spent enough time with OO and classical inheritance knows the problems, but most have never seen how to convert a mess of classes and inheritance into a more functional approach with composition. Don't get me wrong, there was good content here, but I was hoping that after the conclusion, the post was going to go into how to use composition as an alternative.
- timr 13y ago"the real world doesn’t work that way. It really doesn’t work that way. In zoology, for example, we have penguins, birds that swim. And the bat, a mammal that flies. And monotremes like the platypus, an animal that lays eggs but nurses its young with milk." Former biologist here. Actually...most living things do work that way. A human ISA primate. Genetically. Functionally. If you merely focus on outward behavior, you get to the same place that early biologists got to with what was called "morphological classification" -- you find the weird examples of convergent evolution (e.g. "OMG egg-laying mammal!"), and you're tempted to throw out the whole classification system, even though it mostly works, and the errors are merely distracting from the inherent truth of the idea (that we're all related by genetic phylogeny; we literally share implementation). Anyway, programmers, learn from biology: when you see these kinds of errors it probably means that you're classifying things incorrectly, not that you should stop classifying altogether.
- ericHosick 13y agoWhat something is can change greatly based on context. This means how we classify them (generalize/group) can also change. An apple may be used to grow a tree, as a prop in a game, as fertilizer or consumed. That being said. Classifying things using inheritance leads to a software architecture that is difficult to change. This is especially the case if you follow the principal of don't break interface. In the real world, people can build out classification systems and easily add in edge cases when we find new ways things can be classified. In software, that could lead to a complete change in the software architecture.
- Jare 13y agoBravo sir, that was a fantastic and succinct explanation of the difference between inheritance in the real world vs in OOP.
- RyanZAG 13y agoThanks for the input - the more cross disciplinary knowledge flow the better. I like your take away in particular. The author is basically saying 'All this OOP stuff that most of the industry has been doing for 20 years? All wrong! Do it my way.' Hearing that biology still uses directed non-cyclic graphs for classification (eg, single inheritance) shows just how powerful single inheritance really is and that we maybe shouldn't be so fast in throwing it away because it doesn't match something. I think sticking to the 'age old wisdom' of using direct inheritance for IS-A relationships and composition for HAS-A is still the right way to go. It's been tested, it works, and using HAS-A for everything makes for less understandable code imo. Often you can fix an IS-A by simply refactoring your graph with a better understanding of the problem space - as it turns out biologists have done as well.
- golergka 13y ago> That kind of ontology is useful for writing requirements, use cases, tests, and so on. But that doesn’t mean that it’s useful for writing code the code that implements bank accounts. Isn't good, easy readable code look very similar to requirements it was written upon?
- platz 13y agoThis is interesting, reading some of the linked documents, my naive generalization is that it pushes the entities into using things like maps and dictionaries instead of static properties. The "system manager" stuff just pulls things out of the maps and feeds them to functions to do work, so it is very much 'data-driven' and I must assume more work is put into "configuration" of the entities, just like a data-driven business process requires "configuration" of the order processing pipeline. http://www.richardlord.net/blog/what-is-an-entity-framework http://www.richardlord.net/blog/what-is-an-entity-framework
- platz 13y agoWonder if this implies you know what you are going to be doing in the future. As said by Sandi Metz, the true value good design is to reduce the risks of future change. I think it is a valid endeavor to study what reduces risk and what increases risk. This doesn't have to be about "design patterns".
- vermooten 13y agoPlease can yo make the type in your blog even less readable? I think knocking back the grey so that it matches the background should do it - you're almost there just needs a tad more.
- dang 13y agoThis sort of sarcastic sniping is one thing we really don't want on Hacker News. We don't ban people for it, but it should be downvoted. I don't mean to pick on vermooten here; lots of HNers post comments like this. Please don't post comments like this. Re-read what you post and, if it contains sarcastic sniping, edit it out. I'll be pointing out examples of what's good and what's bad on HN, in the hope that the feedback will be helpful to the community. When I do that, I hope everyone understands that it's never personal, only about the content and only for trying to make HN better.
- iamwil 13y agoThat inheritance doesn't get encapsulated was the same way I felt about modules in ruby. You could include them into a class or other modules, but the interface was never well defined, and you could cause incompatibilities by relying on the implementation of the class you include into, unless you're disciplined enough to only use methods, and not attributes. On the other hand, defining all the interfaces all the time, like in java was painstaking. I hope for some sort of middle ground.
- gboudrias 13y agoIn javascript* I think this highlights a problem with javascript more than OOP: We're trying to fit it into uses cases that are simply too complex for its design. It wasn't made to build your goddamn bank accounting system, it was made so that "nonprofessional programmers" could animate things on websites. But it also highlights a problem it doesn't take about: Language in computer science and how it affects how we think about things. In this instance, public and private are terrible names. They had to tell us in programming classes that they're not related to security, which means that the privacy metaphor is a terrible idea because it's not instinctual. This in turn causes us to shoehorn class design into things they perhaps shouldn't be. At this point, for complex programs we should be describing things in a much more complex way than "accessible from the outside or not". As the article points out, it doesn't matter that the classes are external, because you can just as well break things from the inside. This is a complex problem, but I think the beginning of a solution is to a) Depend on meta-information (or better implement a flexible, non-arbitrary "access" structure) and b) Use the right tool for the right job, in this case not JS.
- SixSigma 13y ago"Object Oriented design is the Roman Numerals of Computing" - Rob Pike For more quotes see : http://harmful.cat-v.org/software/OO_programming/ http://harmful.cat-v.org/software/OO_programming/ Another, seeing as it's PG : "The phrase 'object-oriented' means a lot of things. Half are obvious, and the other half are mistakes." — Paul Graham
- ternaryoperator 13y agoWhile I admire both Pike and Graham, I've never liked the use of zingers to articulate a programming point. Invariably, programming involves trade-offs, acceptance of limitations in one dimension to gain benefits in another. So, zinging the limitations is easy to do and doesn't advance understanding.
- deleted 13y ago[deleted]
- foobarz24 13y agoThis reminds me of a design question I recently faced when building a simple multi-player game. The idea was pretty standard; The server sent events (player dies, player moves to pos, etc.) to its clients over a TCP socket. When programming the client in Java (for Android) I wanted a clean way to update the world based on the event type. In Haskell I would have done something like data Event = PlayerDied Player Reason | PlayerMove Player Coords | ... and use pattern matching on the event type. In C I would have used a combination of unions and structs with an event type. But how to do that in Java? I ended up with interface Event { public update(Game g); } and used e.g. class DeathEvent implements Event { DeathEvent(String player, String reason) { ... } update(Game g) { g.killPlayer(player, reason); } } Combined with a parsing function (public Event parse(String line) {...}) I could read from the socket and update the game in a convenient way, but to be fair I used that mostly because Java guys discourage you to use instanceof although it seemed clearer. So is this the preferred way to do something like this? I think "they" (the OOP warriors) call this the Visitor pattern. However I really find the data-type encapsulation in Haskell and other languages (in Python a tuple (type, object) would do) superior. But maybe my Java just got rusty.
- mcv 13y agoIsn't he mostly complaining about lack of encapsulation? So what if you do encapsulate your data? That's totally possible in most programming languages, and it's even possible in javascript if you drop prototype inheritance. You can use closures to encapsulate your data.
- deleted 13y ago[deleted]
- sgy 13y agoIt's pretty much a promotion/defense for SmallTalk. Why everybody has grudges against JavaScript?
- monokrome 13y ago"Hey, guys. Here's a really poorly considered hierarchy of classes, where I haven't really made any real effort to separate concerns or otherwise prepare for the problems which I am specifically creating. Now, look at all these bad things that I've done! NEVER DO ANYTHING SIMILAR EVER." That pretty much sums it up.
- dangoor 13y agoSomething I haven't seen mentioned in this discussion so far is the Data, Context, Interaction (DCI) pattern. https://en.wikipedia.org/wiki/Data,_Context,_and_Interaction https://en.wikipedia.org/wiki/Data,_Context,_and_Interaction As mentioned at points in this thread, some objects need different behavior based on their context. DCI is a thought provoking way to represent that (though one that not in common use and that is often described as "being done wrong" on the object composition mailing list[1]). [1]: https://groups.google.com/forum/#!forum/object-composition https://groups.google.com/forum/#!forum/object-composition
- benrhughes 13y agoAlthough I'm very, very sympathetic to the idea that class hierarchies often cause more pain than they're worth, using JS to make that point is a bad idea. In most actual OO languages, there is a clearly defined interface between a base class and it's inheritors: eg in c# you have protected (to expose state), abstract to force implementation and virtual to optionally allow implementation. In no way are you forced to expose all internal state (even though people often do). I think there's a case to be made against class hierarchies, and also against using OO in javascript. But I'm not sure Ragan made either of them well here.
- EGreg 13y agoFrom a practical point of view, mixing in behavior and implementing via encapsulation is usually easier to maintain and extend than inheritance.
- tristan_juricek 13y agoThis approach to discussing inheritance treats JavaScript as if you're a Java or C++ programmer, and completely lacks any clarity with how inheritance in JavaScript really works. Best intro I've read on the topic I'm talking about is from Alex Sexton (just read the last example, it really hits the nail on the head): https://alexsexton.com/blog/2013/04/understanding-javascript-inheritance/ https://alexsexton.com/blog/2013/04/understanding-javascript... So you might want a wee bit of hierarchy, if you're thinking like those "shared options" scenario, but not in the "OO type abstraction tree". So, you might have something like a "Account.prototype.primeInterestRate" property that you can change in a running program, and then all the other types of account can calculate interest based on that shared property. However, the more experienced jS developers I've met might take those "Account.prototype.balance" and "Account.prototype.deposit" methods, and push those into a "mixin" type (like "CurrentBalance") where those methods are copied (not inherited) onto the child class prototype, and those methods, might have initializer helpers to set up the "currentBalance" property they use. This mixin approach only gets gnarly if there's any feature envy. (Document your object properties clearly, folks. This is where javaScript's flexibility often becomes a crutch - lots of issues happen if mixin code uses "this.foo" for different things.) Anyhow, what's interesting here is that Account carries the property that's shared, but CurrentBalance carries "behaviors", and is not shared, and your "child classes" like VisaDebitAccount embed both in different ways. It is a very different way of thinking about object relationships, and often works smoothly. But if you're using classes in JavaScript like you would Java, well, then, you're not really using JavaScript, right? And, this whole talk about biological-style ontology just becomes the wrong metaphor, because while "humans are a primate" we can't change aspects of primates to add behavior to people!
- einhverfr 13y agoWhat is missing from the discussion here is that where domain knowledge is less important. For example, almost every widget system I have ever looked at uses class inheritance because it makes it relatively easy to manage consistent interfaces across classes. This is true of GTK, wxwidgets, and many more. It is true that the natural world doesn't necessarily admit of perfect neat classifications generally, much less trees. However, when we are talking about purely engineered solutions, the same arguments don't apply in the same way. Here's an example. In LedgerSMB 1.4, we use (shallow) class inheritance in a reporting framework. It works, and works well. Reports inherit an abstract class with lots of hooks for customization but a lot of defaults. In future versions we will likely be moving away from an inheritance-based approach, not because of the arguments here or the maintenance issues (which will crop up any time you rely on external components) but because we think we can create clearer code by moving from a class/inheritance approach to a DSL approach. I am not sure that class contracts and DSL syntax contracts are necessarily any different from a maintenance perspective other than the fact that the latter strikes me as resulting in clearer code.
- protez 13y agoClass hierarchies are just mental tools, not how machines/programs/automatas actually work. If the mental tools implode due to exceptions and complexities, that's the problem of their uses, not tools by themselves. Before blaming hierarchies, you should blame yourself for using tools in wrong ways.
- ajuc 13y agoClass hierarchies are OK when they have one level. It's essentially object oriented switch statatement with bells and whistles. I can't remember one class hierarchy more than one level deep that was worth it. They save a little code - true, but at the cost of coupling, making it harder to change, and forcing early debatable decisions on programmer (which classification is more important and goes first for example).
- twfarland 13y agoMy js style began with a heavy usage of classes. I then left that for prototype composition. Recently, I've arrived at a style influenced by Haskell's separation of functions and data - namespaced objects of functions that act on plain json-serializable data. Flexible, simple, and perfomant. I don't miss 'this' at all.
- V-2 13y ago"Only, the real world doesn’t work that way. It really doesn’t work that way. In morphology, for example, we have penguins, birds that swim. And the bat, a mammal that flies." Yes but we have Single Responsibility Principle and while an animal is one object in physical world, it doesn't mean it should be a single object in OOP. Start breaking it down... public interface BodyType {} public class TwoArmsTwoLegs implements BodyType {} public class FourLegs implements BodyType {} public interface Locomotion<B extends BodyType> { void walk(B body); } public class BipedWalk implements Locomotion<TwoArmsTwoLegs> { public void walk(TwoArmsTwoLegs body) {} } public class Slither implements Locomotion<NoLimbs> { public void walk(NoLimbs body) {} } public class Animal { BodyType body; Locomotion locomotion; } Animal human = new Animal(new TwoArmsTwoLegs(), new BipedWalk()); (Code sample from an article in Software Development Journal by Łukasz Baran)
- bayesianhorse 13y agoIn idiomatic Python, the only reason for class inheritance is for code reuse. In other languages, especially Java, class hierarchies are a matter of self-esteem. Javascript programmers could do worse than emulate Python in this regard.
- V-2 13y agoYet another post titled "don't do X", which actually reads as "do not exaggerate with X" or "did you know? X has some caveats". Of course inheritance is not a solution for everything. Avoid using deep inheritance hierarchies, you'll paint yourself into a corner. It's advisable to prefer composition over inheritance. You should not be that guy who only has a hammer and everything looks like a nail to him. But it doesn't translate into: "hammer? don't do that!" All the examples he's giving either demonstrate abusing the inheritance concept or just show that it has certain limitations. There are well known solutions and guidelines for dealing with problems such as fragile base class, other than throwing the whole paradigm out of the window