13 ms·
Inheritance Is Terrible
- lispm 11y agoBasically CLOS (Common Lisp Object System) implements Flavors' mixins as normal classes. Example: (defclass priceable-mixin () () (:documentation "we can calculate a price for something")) (defclass standard-priceable-mixin (priceable-mixin) ((price :initarg :price))) (defmethod price ((i standard-priceable-mixin)) (slot-value i 'price)) (defclass shared-priceable-mixin (priceable-mixin) ((royalty :initarg :royalty) (markup :initarg :markup)) (:documentation "the price has two components: royalty and markup")) (defmethod price ((i shared-priceable-mixin)) (with-slots (royalty markup) i (+ royalty markup))) ; we want to sell books and movies (defclass book (shared-priceable-mixin item) () (:default-initargs :royalty 2 :markup 8)) (defclass movie (standard-priceable-mixin item) () (:default-initargs :price 10)) ; there is a cart with items and a total price (defclass cart () ((total :initform 0 :accessor cart-total) (items :initform nil :accessor cart-items))) (defmethod add-item-to-cart ((i item) (c cart)) (push i (cart-items c)) (incf (cart-total c) (price i)))
- SCHiM 11y agoI feel like this article attacks a strawman. Obviously there are going to be times when <feature> is not the right tool for the job, in that case you don't universally bash <feature> you simply don't use <feature>. The problems with inheritance in this article seems largely fabricated to make it look bad, while in reality (IMO) it's a very elegant and useful tool in any programmer's toolbox.
- putzdown 11y agoMany, many programmers don't really understand inheritance, and that fact is terrible. Many who don't understand inheritance think they do, and therefore believe that their bad designs are characteristic of all designs, and that misunderstanding is terrible. Terrible inheritance is terrible. Good inheritance is good. To enjoy good inheritance, first learn to think in layers of abstraction. If this is hard for you (it's not easy for anyone), either train yourself to do it or don't use inheritance—but then don't criticize inheritance for your own shortcomings. Second, make each class properly express its proper abstraction. Abstract things like "items" should be very abstract. They might have a price but have no idea how price is calculated. More concrete things like books and movies should have more concreteness, but no more than necessary. Often, "deep" layers of abstraction really are little more than interfaces. All the major languages have ways of expressing interfaces as distinct from classes as such, but there is no distinction between them at heart: an "interface" is just a very abstract class; that is, it offers methods without (or with few) implementations. Whether you use "interfaces" or "protocols" or "abstract classes", your language probably offers ways of expressing "lots of abstraction" as well as ways of expressing "more concreteness". Think in layers of abstraction and then use all the tools available to express that thinking directly. When requirements, or insights into the design should work, change, then naturally anything can change, from interfaces to detailed implementations. No tool, language, or technique will prevent change, but a clear mental model of what is being expressed and code that succinctly and directly expresses that mental model is your best basis for coping with change. Don't blame inheritance. Don't, for crying out loud, praise interfaces when they are simply a purified form of abstract inheritance. Learn to use the tools that have served millions of programmers well for twenty or thirty years and then the tools won't seem so dangerous and strange. Or, if the tools really just don't suit your way of thinking, don't use them; use other tools; but either way don't blame the tools.
- guelo 11y agoI had trouble following along because I couldn't get over how backwards the code is. Why should a book object have the responsibility of adding itself to a cart? Instead of calling book.add_to_cart(cart) It should be cart.add(book)
- wvenable 11y agoI think it ruins the whole argument. It is not an argument against inheritance but an argument against bad coupling masquerading as an argument against inheritance.
- nitwit005 11y agoI get the point being made, but the rules probably shouldn't be directly associated with the Item objects in the first place. How would you apply a pricing rule like: "Free shipping if item total is > $20 after discounts applied"? The general approach I've seen to this sort of thing is to create a stack of rules, and each rule applies its changes if needed. And for that, an array of function pointers will work fine.
- decker 11y agoMore like being terrible at using inheritance is terrible.
- jack9 11y agoInheritance was wrongly added to the OOP paradigm by convention, not theory. Languages that tried to implement OOP used "classical" inheritance (C++, Java, Pascal, Python, PHP, etc), so it was handy to say the 2 concepts were related when they were never intended to be. "Actually I made up the term ‘object-oriented’, and I can tell you I did not have C++ in mind." — Alan Kay That's the pre-history. The more recent history is that classical inheritance fought and fought to avoid multiple inheritance. Interfaces were poor substitutes that still only act as API signatures to match, rather than providing orthogonal-inheritance properties like a logging interface. Naturally, over time, composition based multiple inheritance exists in almost every classical inheritance language ... or soon will as codifying common mixin behavior just allows for better compiler optimization (https://en.wikipedia.org/wiki/Trait_(computer_programming) https://en.wikipedia.org/wiki/Trait_(computer_programming). The interesting question is if Classical inheritance or Prototype inheritance is superior and on what KPMs? Javascript, Lua, Erlang, and more have had a lot of programmer time spent on trying to convert the prototypical systems to classical inheritance idioms. Is this because most programmers are poor and the poor programmers reason better with a flawed system? Is it because they can't comprehend prototypical chains clearly? Is it because of the positioning/dominance of prototypical languages in their respective tech niches that they still exist? There's lots to be said about the weaknesses of inheritance, but it's far from terrible. Programmers are lazy and so far, it's been the ideal paradigm. These blog posts/youtube videos/rants just seem pathetically misguided. Once someone can provide some real data, an opinion based on the interpretation of that data is something worth reading.
- gpderetta 11y ago>"Actually I made up the term ‘object-oriented’, and I can tell you I did not have C++ in mind." — Alan Kay And that's why C++ traces its origins to Simula, which predates Smalltalk.
- PaulHoule 11y agoFrom the viewpoint of semantics the problem is that things don't categorize out in a strict tree. The Borges encyclopedia list http://avery.morrow.name/blog/2013/01/borges-chinese-encyclopedia/ http://avery.morrow.name/blog/2013/01/borges-chinese-encyclo... is actually a list of decent categories (for the most part you can say if an animal is a member of a category or not) but they are not mutually exclusive, do not tile the space of all animals, etc.
- paulmd 11y agoAnimal taxonomy is actually much weirder than you would think at first glance. The classic definition of species is the group of organisms which are capable of producing fertile offspring. However, there are also ring species, where you have populations A,B,C where B can interbreed with either A or C, but A cannot interbreed with C. Or in an alternate phrasing, members of B are members of both species A and species C. This obviously becomes intractable beyond a very few interbreeding populations. So really, even nature favors composition over inheritance.
- jwdunne 11y agoThis is really interesting. I've been turning this idea over in my head every now then as a mental exercise (bus ride material). One, it validates my reasoning that rigid single-inheritance trees are a bad representation. What my mind kept turning to was the possibility of Neanderthals and early humans interbreeding yet being different species that had a common ancestor. It isn't really a tree in that case. Two, this comment gives my past time more food for thought!
- PaulHoule 11y agoin fact the tree of life can't be traced back to a single root because us Eukaryote organisms are the result of a train crash between Eubacteria and Archaea maybe 2 billion years ago.
- mediaobelia 11y agoBiological taxonomy is just not a good analogy. Object behavior hierarchies aren't analogous to any real world thing, IME. They are what they are.
- yummyfajitas 11y agoI don't understand his comment, "I'm disappointed to see that Scala has largely preserved Java's inheritance model." This directly translates to scala: trait Priceable { def price: Int } def addToCart(cart: Cart, item: Priceable) { cart.increaseTotal(item.price) } class Book extends Priceable { ...same thing... } The only thing non-idiomatic about this in Scala is the use of a mutable cart.
- lbarrow 11y agoTraits are great, but Scala still has subclassing. I'll update the article to make it more clear -- thanks for pointing that out.
- jpitz 11y agoSubclassing isn't evil. Subclassing which violates Liskov Substitution, now I'm listening.
- noobermin 11y ago>but Scala still has subclassing C++ has goto and labels, and although they are not needed for the most part in modern C++, they are still there. The key here is the programmer does not need to use them.
- n0us 11y agoI find that inheritance isn't really necessary in Python for most cases due to the ability to define magic methods which are basically interfaces. One place I have seen it used effectively is in the Django ORM where it makes sense to use subclassing.
- ced 11y agoI'm not sure I get his point. Is it that "A member (field) of a class is not a proper interface"? In Python, that's not really true, since he could have used a property and kept his first design. How is that significantly different from his final solution?
- JoshTriplett 11y ago> I'm disappointed to see that Scala has largely preserved Java's inheritance model. Scala builds on the JVM and exposes the Java standard library. It doesn't have much choice in supporting that model. In theory, Scala could provide additional support for an alternate model, but that would add complexity without taking any away, and would require defining how the two models interact.
- lbarrow 11y agoThat's a fair point. Scala definitely seems to have had to compromise the quality of the language features it offers in order to achieve Java interop.
- pjmlp 11y agoWhy are those things still being rediscovered? The fragile base class problem is a very well known one since the 90's. Already in the 90's we had programming languages like Component Pascal, whose designers pushed for the idea of component based programming. Basically what nowadays is known by using COM interfaces, regular interfaces, traits, protocols, whatever. The first edition of "Component Software: Beyond Object-Oriented Programming" is from 2002: http://dl.acm.org/citation.cfm?id=515228 http://dl.acm.org/citation.cfm?id=515228
- marblar 11y agoInheritance is the first thing I learned when I started learning object oriented programming.
- pjmlp 11y agoAnd correctly so, but the teachers should have also teached that there isn't a single way of doing OOP and what are the cons of certain design approaches.
- manyxcxi 11y agoAs well it should've been! If you're doing OO, you're kind of missing the reason for it if you aren't aware of inheritance, isolation of concerns, and polymorphism. Granted, you could have a lot of copy/paste code and NOT use inheritance, but then why use OO in the first place? EDIT: I mentioned in another comment, and a commenter to this correctly noted- it's probably because it's easier to understand, and a fundamental piece of OO. It's actual usefulness in the real world varies, but it is also how one would step into understanding interfaces, composition, and other aspects of OO that get used heavily in OO in the real world.
- sotojuan 11y agoI think what he means is that in school we are taught inheritance and nothing else, so many programmers only know it's "pros" and not why other techniques like composition can be beneficial.
- mjevans 11y agoThis would have made more sense if the inheritance were different. Item (a thing that is sold at a final price and which goes in to a shipping container (has physical size / weight data)). Book (a type of Item) which has a price that decomposes in to various costs... BD_Movie (a different type of Item) which has other features that decompose differently from other Items. Though an argument could also be made for simply having a generic ShippableItem object which has extra key-value tags in that same application. Maybe the issue isn't that the ability to use Inheritance is terrible but that the particular design choice of doing so turned out to be a poor fit for expressing the business needs.
- Gibbon1 11y agoMy eyes start bleeding when I think too hard about this, but seems in my world inheritance in these cases is being used as a really bad substitute for properties. Which is a lot of 'things' have flat collections of properties not a hierarchy of properties. I suspect this is why LINQ kinda popular. No one cares if book inherits item. They want to query the random collection of crap someone ordered for 'is a book' or is 'food' so they can properly calculate the sales tax in Minnesota.
- dimgl 11y agoThis is a non-issue in C#. In C#, properties have getters and setters. Instead of making a function named GetPrice() you'd simply modify the price getter to include the additional fees.
- bunderbunder 11y agoYou've pointed out a distinction without a difference. On the back end C# properties are implemented as a pair of CLR methods named get_[PropertyName]() and set_[PropertyName](). The rest is syntactic sugar. So when you "simply modify the price getter", what you're doing is simply modifying the implementation of a method named get_Price().
- dimgl 11y agoRight, but the article is arguing the inconvenience of having to make such methods across all classes that derive from a superclass. If this is inherently provided in the language then I would say that language doesn't suffer from the issue discussed in this article. And that language I'm speaking of is C#.
- manyxcxi 11y agoWith respect to Java, the getters and setters are automatically generated in an IDE if you want, so it's not all so bad. And if you get to a spot where your getter and setter needs to do more than spit out the value or assign it for some reason, you're really at the same amount of code. It is definitely nice in C# not having to LOOK at all the damn getters and setter though.
- jfoutz 11y agoIt's not so much about that specific case, any other side effect will get you in trouble. it's more about how 'extends' introduces 2 tricky features. 1) you get a neat dispatch mechanism. if foo doesn't have a method check foo's superclass, and see what's available there, recursively until you run out of parents. 2) you can slip a different implementation in to some complex system, and they never need to know. A nice example might be a DatabaseConnection, and a TestDatabaseConnection, that doesn't actually talk to a database, but logs what was requested. The thing that sucks about inheritance is, you never really have a DatabaseConnection, you wind up having a MYSQLDatabaseConnection or a PGSQLDatabaseConnection, that take slightly different syntax. It's not real clear if you should make a TestDatabaseConnection for each database permutation, or just make a logging connection. Anyway, traits can fix this up a little nicer than interfaces and delegates can. Inheritance is helpful, but it kind of sucks because you only get to use it once, and it mixes two different concepts together. "easy" API extension by tacking on more features, and the cuckoo's egg that lets you slip different implementations in without a recompile. IMHO #2 is really what people are after, and you can get that by just using interfaces for everything.
- ChuckMcM 11y agoSo when I read this, "Later, however, the business needs to start tracking the royalty paid to the author of a book separately from the rest of the price." I felt the author is conflating "inheritance" with "requirements". I don't think anyone would argue that if you design system, and then change the requirements of what the system should do. You have two choices; build a new system that meets both the old and new requirements or patch the old system to kind of meet the new requirements. And I get that this critique is looking at the example perhaps more literally than the message, which is that it is hard to make systemic changes to object oriented systems, but that criticism of OOP is well trodden. Bjarne and others will tell you that it is easy to make reliable and testable additions to an OO system and that is its strength. Refactoring such systems in the face of new requirements will always be hard, and yet the argument goes that if you do so correctly you will be able to re-use tests from the previous system to verify it still works the way it did and write new tests to show that it works the new way as well. The example of books and movies is a good one because shows how initially the thinking that every item has a price, but what should have been considered is that some items are of a type 'media' that has other attributes that regular items don't. If you are familiar with the joke[1], it is the dichotomy between a toaster and a "cooker of breakfast foods". [1] http://www.danielsen.com/jokes/objecttoaster.txt http://www.danielsen.com/jokes/objecttoaster.txt
- cubano 11y agoWhy not just bake the various Price components right into Item, besides the get_price action? Every Item should have a price (ie the get_price stub), so I don't think it would be bad design to include it. If Item includes a Price object that has all the possible Price properties, it could be easily modified as specs change.
- danso 11y agoI think the problem is that, typically, all the various Price components are not known a priori when Item is first implemented. They become known or realized as more Item subclasses are created. If a software dev group actually knew all of their future requirements before implementing a framework...well, a whole lot of software engineering problems would be mitigated :)
- cubano 11y agoOk, but it surely can be known (and obviously was due to the get_price() interface stub) that every Item will have a Price, so I stand by my intuition that a Price object could be included in Item.
- danso 11y agoA little OT...but does anyone have insight into the historical reasons of why inheritance seems to be the default entry point into teaching/explaining OOP and classes? I remember learning all about inheritance in school but only tangentially about interfaces. And looking at explainers/tutorials in the wild, it seems that many/most start out with the classic Duck > Animal or Thing > Car examples. I want to chalk it up to inheritance being an inherently easier thing to think about in real world terms, with a name that has easily-understandable connotations, even if some of those connotations are probably harmful in the way they muddle up via leaky abstraction when thinking computationally. Whereas with mixins/interfaces...those terms have never made immediate sense to me. And yet when it comes to implementing OOP, mixins/interfaces seem so profoundly more suitable as the go-to strategy that I wish I had learned exclusively about them, with inheritance being left for edge cases. Maybe I just had a bad curriculum or bad memory when it came to the lessons about interfaces. But if there is a stronger focus on inheritance when it comes to teaching OOP, is it due to the design of C++/Java? Or just the practicalities of teaching OOP? Teaching about classes seems like a good first step in teaching OOP, and teaching about inheritance requires fewer new concepts/syntax from defining a class. Whereas mixins/interfaces require introducing the concepts of modules.
- parenthephobia 11y agoC++ doesn't have interfaces, so it's quite understandable you wouldn't learn about them if you were learning about C++. Java-as-taught is often ancient Java, from before enhancements like default interface methods. The only non-annoying way to inherit behaviour in ancient Java is inheritance. Even modern Java lacks automatic implicit composition ala Go. Java also encourages large interfaces with many methods. Without any ability to inherit behaviour for those methods, it's often less tiresome to inherit from a class instead. Go, by way of contrast, encourages tiny interfaces with, ideally, one or two methods. In this example, we might suppose that a quintessential "vintage" Java-esque design wouldn't have a single-method "Priceable" interface. It'd have a CartItem interface with hundreds of methods, all of which you'd have to implement - even if only to stub out with exceptions - in order to implement it. So you wouldn't actually use an interface, or else you wouldn't be avoiding all that typing.
- parenthephobia 11y agoHis example is bad. class Item def add_to_cart(cart) cart.increase_total(this.price) The fault is really that Item.price isn't defined, so it can't be assumed to exist in Item.add_to_cart. When he later adds get_price to all subclasses, he is really just fixing that bug. In a language like Java, this problem could never have arisen in the first place, since the compiler would know that Item.price didn't exist. In a language like Ruby, it isn't a problem. Ruby doesn't expose "fields" and methods differently, so the consumer of Book doesn't need to know that the price "field" is now a method that computes the price. That's proper abstraction. His interface-based solution solves nothing, because exactly the same sequence of events would have unfolded if he'd originally been using an implicitly defined price field. If he then needed to make that a method, he'd then need to define an interface for get_price and then make all the things that should be Priceable implement that interface[1]. [1] And this bypasses one of the principle advantages of Go's interfaces. You don't say that a type implements an interface; types automatically implement an interface simply by having the appropriate methods.
- danidiaz 11y agoAn interesting aspect of Haskell is that it tries really really hard to avoid subtyping, relying instead on other mechanisms. This talk by Simon Peyton Jones touches on it from minute 40 onwards: https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=3640b30a-38b9-49a6-b95c-d96e93bc9284 https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?... "In a language with generics & constrained polymorphism, do you really need subtyping too?"
- marcosdumay 11y agoHaskell has interface based generics, just like the article wants people to write. It just calls its interfaces by classes, and has a more powerful implementation than Java, that deals with default code and makes incompatibilities impossible. Haskell even allows interface inheritance, just like Java, but calls it by... well, no name that I'm aware of, but it's there.
- wai1234 11y agoThe first implementation was weak from the start. By exposing the instance variable directly, you threw away all the power of inheritance and guaranteed a future meltdown. If you had correctly implemented setters and getters from the beginning, you would have been perfectly prepared for what would have been a minor and appropriate change to the pricing scheme that are the whole point of encapsulation. You wrote procedural code using an object-oriented language. Don't blame a fundamental design approach for your lack of good implementation. "a lot more code" - Really?
- deleted 11y ago[deleted]
- yxhuvud 11y ago"On the surface, this looks reasonable." No, it actually doesn't. class Item def add_to_cart(cart) cart.increase_total(this.price) This is where the trouble is. It is not the inheritance, it is the add_to_cart method not being in the correct class. If this instead had been class Cart def add(item) .. add item to some collection of stuffs implementing price(). def sum() .. calculate total sum. then the whole problem evaporates. If your code reads do_something_to_foo(foo), then very often usually it really ought to be foo.do_something() Inheritance can be ok when used sparingly, but this example is not one of those.
- cubano 11y agoYes totally agree. The best way that I learned OO was when it was explained that, in most cases, its best to take objects as nouns (ie Item, Book, Prices etc) and the object methods should be verbs (ie do_something() or act_on_this() etc)
- thomasahle 11y ago'item.add_to(cart)' seems to work fine from a gramatical point of view. It's only when we think in terms of ownership "The cart should own the item" that we choose the right order, no?
- wvenable 11y agocart.add(item) is also grammatical and less conceptually complex than any sort of "add_to" construct.
- enraged_camel 11y agoYep. There is no reason the Item class should "know about" the Cart class. The add_to_cart method doesn't belong in it.
- lbarrow 11y agoI concede the example is a bit of a straw man. But I'd challenge you to provide a similarly concise problem and solution where 1) inheritance is useful and 2) there isn't a better solution that doesn't use inheritance.
- halayli 11y agoInheritance isn't terrible if you design your classes properly.
- noobermin 11y agoI remember learning OOP back in the day. Personal projects from scratch took forever because I kept imagining more edge cases which required more and more abstract ABCs. And I felt the reason I couldn't work with all this OOP stuff was because I wasn't as smart or devoted to it. I'm not saying I'm smart, but it's funny now that it at least seems like the whole "modern approach" is anti-inheritance and more about "interfaces" and composition, which essentially is what I did anyway because inheritance OOP got me nowhere.
- manyxcxi 11y agoAs mentioned elsewhere, interfaces really a form of inheritance. They just generally have no implementation code in them. I also very much understand the thrashing about that occurs when trying to abstract and inherit and compose my classes while making progress on a new idea. I think I early on solved it by just ignore trying to do everything The Right Way and write code that works to the solution. Then as the code evolved I could easily see where code was duplicated and begin grasping where things should be sharing code in some way. It greatly improved my efficiency AND made my code cleaner in the long run.
- kstenerud 11y agoHere's how I've experienced languages, technologies, and paradigms over the past 20 years of my professional career: Step 1: settle on technology X because it fits in rather nicely for our requirements. Step 2: The requirements change a bit. Mostly you can handle it elegantly, but there are some rough edges. Step 3: More new requirements. Now things are starting to look pretty ugly in places. Evangelist B says "See? I told you technology Y was the right choice! Now look at this mess!" Step 4: Go with technology Y on the next project. Step 5: GOTO Step 1
- ChrisRus 11y agoI've added comments to the OP but to summarize: OO inheritance is useful for codifying a limited set of real-world models. But to truly generalize, we need to stop pretending that it's feasible to make progress without actually investing the effort to codify state v time behavior contracts over software. Today, if anyone bothers to even try to document these behaviors, it's done inconsistently and not in a form that's readily usable by tools that help you through the maze of detail. This is a shame. Re-usable software requires (a) strong contracts for data I/O (what we call API) and (b) state v time contracts in _machine readable form_. Conceptually, no hardware system on earth would exist if not for the fact that both (a) and (b) are formally defined. Don't take my word for it: go read up on state-of-the-art SoC design and IP blocks and ask yourself if exchanging _text files_ (IP blocks) is really any different than software. I think it's not.
- deleted 11y ago[deleted]
- mchahn 11y agoI understand now why a GO feature list I saw recently included "no inheritance". I thought it odd that a lack of something was listed as a feature.
- teh64 11y agoThis can be done in Java just fine: public interface PriceAble { int getPrice(); } class Book implements PriceAble { int royalty = 2; int markup = 8; @Override public int getPrice() { return royalty + markup; } } class Movie implements PriceAble { int price = 15; @Override public int getPrice() { return price; } } class Cart { int cartTotal = 0; void increaseTotal(PriceAble priceAble) { cartTotal += priceAble.getPrice(); } } public class forExample { public static void main(String[] args) { PriceAble movie = new Movie(); PriceAble book = new Book(); Cart cart = new Cart(); System.out.println(cart.cartTotal); //prints 0 cart.increaseTotal(movie); System.out.println(cart.cartTotal); //prints 15 cart.increaseTotal(book); System.out.println(cart.cartTotal); //prints 25 } }
- DrScump 11y agoPerhaps the Brian Will anti-OO videos provide more useful examples: https://www.youtube.com/watch?v=QM1iUe6IofM https://www.youtube.com/watch?v=QM1iUe6IofM "Object-Oriented Programming is Bad" https://www.youtube.com/watch?v=IRTfhkiAqPw https://www.youtube.com/watch?v=IRTfhkiAqPw "Object-Oriented Programming is Embarrassing: 4 Short Examples"
- pier25 11y agoTry to eat a steak with a hammer, and then argue that hammers are terrible. No shit Sherlock! Don't use inheritance to solve all your OO problems. Ever heard of Design Patterns? https://en.wikipedia.org/wiki/Design_Patterns https://en.wikipedia.org/wiki/Design_Patterns
- cdevs 11y agoIsn't this the talk that leads to the creation of setters and getters. If all items implement a getPrice() function then the caller doesn't care about what mix of vars adds up to that items price in the background, return royalties+tax+whatever..
- based2 11y agoLet's assign values in verbal direction. Other ideas, with traits: > Statistical traits combinaisons: class City with % Sinjar, Theba, Techonolochikan % -> better auto tests (33/33/33) -> 3 tests => ! data config, not logical structure class Cluster with % Node1, Node2 % class Cluster with % 25 Node1, 75 Node2 % => class Cluster with % % = 25 Node1, 75 Node2 > Dna traits combinaisons: class Dna3 with Θ dna1, dna2 Θ https://en.wikipedia.org/wiki/Theta https://en.wikipedia.org/wiki/Theta
- totemizer 11y agoI just leave this here: http://www.smashcompany.com/technology/object-oriented-programming-is-an-expensive-disaster-which-must-end http://www.smashcompany.com/technology/object-oriented-progr...
- Tiquor 11y agoBad strawman is bad.