Giter Club home page Giter Club logo

Comments (33)

ericelliott avatar ericelliott commented on May 2, 2024 14

šŸ‘

from clean-code-javascript.

jokeyrhyme avatar jokeyrhyme commented on May 2, 2024 11

Eric Elliott sold you blue water and called it medicine.

@Jeff-Mott-OR let's try to keep things civil and respectful. You can discuss the merits or pitfalls of an idea without maligning the character of someone else.

It's obvious that you are passionate about this discussion, let's just try to be passionate and constructive.

from clean-code-javascript.

Jeff-Mott-OR avatar Jeff-Mott-OR commented on May 2, 2024 6

@kopacki, @Tarabass, @dschinkel

I hate to be the wet blanket, but unfortunately Eric Elliott got his information wrong.

Invented definitions

In Elliott's blog, he literally invented his own personal definition of object composition, and his invented definition is different than how the GoF used the term when they coined the phrase "favor composition". Elliott even cited Wikipedia's article on object composition just so he could rant about how he thinks Wikipedia got it wrong. "Favor composition" is only meaningful if we all agree what that term means, and Elliott's personally invented meaning is in conflict with the GoF and Wikipedia and MDN and damn near every other reference.

It's actually inheritance

What Elliott calls composition is in fact still inheritance. This became especially obvious when, bizarrely, Elliott claimed that class A extends B is composition, even in an unambiguously classical language such as C++. I shit you not.

Multiple inheritance

The killer feature of what Elliott mistakenly calls composition is in fact just multiple inheritance. Python and C++, for example, are a couple languages that natively support multiple inheritance, so the airplane code above could be written in Python like this:

class Airplane:
  def getName(self):
    return self.name

class AirbusA380:
  name = "Airbus A380"
  def getName(self):
    return Airplane.getName(self) + " of " + self.airline

class Cessna:
  name = "Cessna"

# this is multiple inheritance, not composition
class airbusA380(AirbusA380, Airplane):
  def __init__(self, state = dict()):
    self.__dict__.update(state)

class cessnaJet(Cessna, Airplane):
  def __init__(self, state = dict()):
    self.__dict__.update(state)

# create instances of airplanes
emiratesAirbus = airbusA380(dict({
  "airline": "Emirates"
}))

privateCessnaJet = cessnaJet()

print(emiratesAirbus.getName()) # prints "Airbus A380 of Emirates"
print(privateCessnaJet.getName()) # prints "Cessna"

Not immune

Elliott frequently claims that what he mistakenly calls composition is immune to all the problems that plague inheritance, but in fact it isn't immune to any of them -- which shouldn't be surprising now that we know it's actually still inheritance. The airline code above, for example, is in fact still susceptible to the fragile base problem. Let's say the Airplane object is supplied by a library, and the rest is your own code that uses that Airplane object. Then one day the maintainer puts out a new release, and now the Airplane object looks like this:

const Airplane = {
  getName() {
    this.airline();
    return `${this.name}`
  },
  airline() {}
}

The maintainer of the Airplane object is good about semantic versioning, so he updates the MINOR version number, because, after all, from his perspective, this change is fully backward compatible. And yet, even though it's fully backward compatible, it will nonetheless break your code. This is the fragile base problem, an issue that we normally associate with inheritance, and yet here it is.

tl;dr

If you learned about composition or inheritance from Eric Elliott, then everything you think you know is wrong.

from clean-code-javascript.

Jeff-Mott-OR avatar Jeff-Mott-OR commented on May 2, 2024 5

The good news is that Elliott acknowledged for everyone here that what he calls object composition is actually inheritance.

It turns out that what he calls object composition is inheritance plus a couple rules that Elliott considers a best practice, and what he calls class inheritance is inheritance that does not follow those couple rules.

Inherit only one level deep

The first of Elliott's personal rules is that we must inherit only one level deep.

class A extends B {}

This, according to Eric Elliott, is object composition and not class inheritance. But if we inherit two levels deep...

class B extends C {}
class A extends B {}

Only now -- again, according to Eric Elliott -- does this become class inheritance.

This rule is at least somewhat close to a real, widely accepted best practice -- that we should prefer shallow hierarchies. But there's no hard limit on one level, and even shallow hierarchies are still hierarchies. Contrary to Elliott's claims, both of the above code examples are inheritance and neither are object composition.

No overrides

The second of Elliott's rules is that the derived type must not override anything from the base type.

class B {
    fn() {}
}

class A extends B {}

This, according to Eric Elliott, is object composition and not class inheritance. But if we override something...

class B {
    fn() {}
}

class A extends B {
    // Override
    fn() {}
}

Only now -- again, according to Eric Elliott -- does this become class inheritance.

But this time there is no industry-accepted best practice that matches Elliott's personal rule. He thinks following this rule will save you from the fragile base problem, but actually it won't. Third parties can update their libraries in ways that are fully backward compatible and still break your code. Contrary to Elliott's claims, both of the above code examples are inheritance and neither are object composition.

Definitions matter

When the GoF said to favor composition, of course they didn't mean you should code however you please just so long as you label it composition. Eric Elliott is advocating for a very different technique -- in fact the literal opposite technique since Elliott's composition is a form of inheritance -- than what the GoF were arguing for. Folks who learn from Eric Elliott are lead to believe they're favoring composition but actually they're abusing inheritance more than ever.

tl;dr

Eric Elliott sold you blue water and called it medicine.

from clean-code-javascript.

ericelliott avatar ericelliott commented on May 2, 2024 4

@dschinkel Object.create() is just one of a few ways to use prototypal inheritance in JavaScript using prototype delegation.

class extends is another way to accomplish a similar thing.

Constructor functions and new is another way to accomplish the same thing.

All of them create delegation links using OLOO (Objects Linking to Other Objects). Read the links I posted above for more details as well as "Common Misconceptions About Inheritance in JavaScript" for much more on the topic.

from clean-code-javascript.

ericelliott avatar ericelliott commented on May 2, 2024 4

@Jeff-Mott-OR I addressed this nonsense in "The Open Minded Explorer's Guide to Object Composition".

Basically, somebody got it in their heads that the GoF composite pattern is the be-all and end-all of object composition and no other form of composite object building can qualify as "object composition" and started spreading a bunch of misconceptions.

Complete and utter nonsense.

Yes, object composition is a form of inheritance. That's why object concatenation is often called concatenative inheritance. Yes, concatenative inheritance is a form of multiple inheritance, but concatenative inheritance does not necessarily create class hierarchies, unlike multiple inheritance in C++, for example, where it's possible to run into the diamond problem when there are property name collisions.

Yes, it's possible to build class hierarchies using prototypal inheritance techniques. That's what's happening under the hood when you mimic class inheritance in JavaScript. (I.e., class foo extends bar).

Immunity from the fragile base class problem only comes when you avoid hierarchies. The distinctions are made clear in "Master the JavaScript Interview: Whatā€™s the Difference Between Class & Prototypal Inheritance?".

Some people are more interested in winning arguments on the interwebs than trying to understand each other, and so tied to their narrow understanding of a word that they can't see broader applications.

Some people think if you use a word that violates their narrow definition of it, that means literally everything you say about it is wrong.

That is absolute nonsense, of course.

The whole paragraph that ends with "I shit you not" above is such an intentional distortion that I laughed out loud when I got to that part.

Pissing contests aside, composition over class inheritance boils down to rigid hierarchy vs flexibility.

Disagree with the words I use to describe things all you like. Just pick the techniques that lead to the most flexibility and you'll be all right. I don't care if you chose to call it multiple inheritance. Just don't try to pretend that building out a deep hierarchy with extends is a good practice. ;)

"Favor object composition over class inheritance" is a means to an end: build more flexible code.

from clean-code-javascript.

Tarabass avatar Tarabass commented on May 2, 2024 3

But does it really matter what we use? Writing clean code is not about what is the best pattern or whatsoever, but about a mind set on writing code for others etc.. Inheritance or composition can both be written clean, and both have there benefits. And they can easily be mixed up with each other. The examples are easy to understand imo, and architectual decisions are offtopic.

In the case of "Avoid conditionals" the code is not cleaner when using inheritance of composition, but because of the open/closed principle. This is exactly why I opened an issue and why there should be more info about that :)

from clean-code-javascript.

ericelliott avatar ericelliott commented on May 2, 2024 3

There are several options for inheritance in JavaScript, described in detail in "What's the Difference Between Class and Prototypal Inheritance?"

See also "3 Different Kinds of Prototypal Inheritance"

from clean-code-javascript.

ericelliott avatar ericelliott commented on May 2, 2024 3

It's clear you will never be satisfied by any version of composition other than the composite pattern. That's fine with me. You do you.

For any readers actually interested in learning and building software with flexible code, you can read about how object concatenation solves the fragile base class problem here.

Or watch the very good video I linked to in the previous message.

@Jeff-Mott-OR, whenever I say "composition", you have my permission to replace the word inside your head with whatever word helps you sleep better at night.

Let's let these nice people get on with their lives now.

from clean-code-javascript.

Jeff-Mott-OR avatar Jeff-Mott-OR commented on May 2, 2024 3

EDIT: This was in reply to dschinkel, who has since removed his comment, it seems.


I also read You Don't Know JS. With Eric and that, that's more than enough for me to make conclusions on "composition".

I didn't recall YDKJS mentioning composition very often, so I searched it and found this:

var auth = new AuthController(
    // in addition to inheritance, we also need composition
    new LoginController()
);
auth.checkAuth();

The other thing to mention is that we chose some composition to sprinkle in on top of the inheritance. AuthController needs to know about LoginController, so we instantiate it (new LoginController()) and keep a class member property called this.login to reference it, so that AuthController can invoke behavior on LoginController.

Turns out YDKJS's description and implementation of composition matches what I've been describing this entire time. And matches what the GoF described. And matches what Wikipedia described. And matches what the SO links above described.

Just about everyone in the industry has the same understanding of what object composition is... except Eric Elliott.

from clean-code-javascript.

jokeyrhyme avatar jokeyrhyme commented on May 2, 2024 2

Thanks for the links / screenshot, my favourite part is this:

Forget which paintbrush you use to paint with. Look at the painting, instead.

Regardless of the terminology we use, ultimately we're discussing how best to share code between objects in a way that minimises copy-pasting / duplication. Ideally, we only want to fix a bug once, not across 9 different copies of the same function and mistakenly forgetting the 10th copy.

Besides maintenance issues, traditional inheritance features often exemplify the gorilla-banana problem, and techniques that we sometimes refer to as "composition" offer an alternative approach that grants you the banana without mandating an accompanying gorilla.

from clean-code-javascript.

Jeff-Mott-OR avatar Jeff-Mott-OR commented on May 2, 2024 1

Object composition creates has-a or uses-a relationships. Instead of B is A, B has an A or uses an A.

That is correct.

In JavaScript, this [Object composition] is largely accomplished with concatenative inheritance (AKA mixins), a form of prototypal inheritance.

That is not correct.

Let's look at Wikipedia for IS-A.

Type A is a subtype of type B when Aā€™s speciļ¬cation implies Bā€™s speciļ¬cation. That is, any object (or class) that satisļ¬es Aā€™s speciļ¬cation also satisļ¬es Bā€™s speciļ¬cation

When we do a = Object.assign({}, b) or a = stampit.compose(b), then "a" can do everything "b" can do. "a" satisfies "b"'s' specification. An "a" IS-A "b".

There are also code samples on that Wikipedia page. For example:

class A:
    def doSomethingALike(self):
        pass

class B(A):
    def doSomethingBLike(self):
        pass

This is inheritance one level deep with no overrides. You've explicitly said in past discussions that you consider this to be object composition, yet here's Wikipedia explicitly saying this is an IS-A relationship.

Of course, you've already said in your blog how you think Wikipedia got object composition wrong, so I have no doubt you think Wikipedia got IS-A wrong as well. So let's also look at the very links you just supplied:

You do composition by having an instance of another class C as a field of your class

About composition By composition, I simply mean using instance variables that are references to other objects. For example: class Apple { private Fruit fruit = new Fruit(); } In the example above, class Apple is related to class Fruit by composition, because Apple has an instance variable that holds a reference to a Fruit object.

from clean-code-javascript.

ericelliott avatar ericelliott commented on May 2, 2024 1

Fine. Fine. Call concatenation "duct-tape inheritance" for all I care. Re-read this article and replace every instance of "composition" with "duct-tape inheritance".

It's all the same to me.

from clean-code-javascript.

ryanmcdermott avatar ryanmcdermott commented on May 2, 2024 1

Closing this Issue for now, thanks for everyone's contributions šŸ˜„

from clean-code-javascript.

cookiengineer avatar cookiengineer commented on May 2, 2024

The issue #45 seems to be also relevant, as the example with withdraw(amount) seems to imply Composition Pattern.

from clean-code-javascript.

Tarabass avatar Tarabass commented on May 2, 2024

@ericelliott When I saw the example I almost instantly thought about you. I wanted to open an issue as well about this, but could not find your post on medium about why not extend classes. Maybe you can post that link here so we can learn?

from clean-code-javascript.

jokeyrhyme avatar jokeyrhyme commented on May 2, 2024

@gaearon has some great advice about using class:

https://medium.com/@dan_abramov/how-to-use-classes-and-sleep-at-night-9af8de78ccb4#.93lw502p8

It mostly boils down to limiting use of extend and super, and still preferring composition over inheritance.

If I was working in an existing codebase with a traditional classy architecture, I'd certainly prefer to use class instead of the weird hoop-jumping we had to do in ES5 and older.

from clean-code-javascript.

dschinkel avatar dschinkel commented on May 2, 2024

Why no Object.create()?

from clean-code-javascript.

cookiengineer avatar cookiengineer commented on May 2, 2024

@dschinkel Instead of Object.create() clutter, you can also use a simple Object.assign() in combination with mycomposite.prototype = Object.assign({}, other) :)

from clean-code-javascript.

aaronmcadam avatar aaronmcadam commented on May 2, 2024

I think changing this part of the guide is tricky because the traditional replace conditional with polymorphism refactoring uses template method which uses inheritance. Maybe using composition here would confuse more than it would teach.

from clean-code-javascript.

dschinkel avatar dschinkel commented on May 2, 2024

cool thx for the share @ericelliott, reading it now!

from clean-code-javascript.

dschinkel avatar dschinkel commented on May 2, 2024

@Tarabass

but does it really matter what we use?

yes to a degree. Clean Code includes not only the concepts from Uncle Bob, but also practices/styles/patterns in the language you're referring to when talking about Clean Code; not just the concepts of clean code only, IMO.

In any language, there are debates but there are good practices that have clearly been agreed to upon most the community. Lets say I was writing a

clean-code-csharp

Would I use abstract classes everywhere in examples, no way. That's clearly basic code smell (why? because when you do, your entire codebase is coupled, fragile, and hard to test). Not something I'd want to be promoting people to be adopting. There are way too many .NET shops still thinking that using Abstract classes throughout their entire codebase is the way to go. And you end up with a coupled mess.

While not everyone is gonna agree on what has been put forth, that's fine, but there are clearly some very bad practices that shouldn't be shown as a way to implement clean code that are very very obvious to the masses.

As our profession matures, there are clearly a set of "code smells" defined for every language by now. Some that nobody can really argue IMO. For example, don't use output params in C# methods...those are just horrible.

Then obviously there is a huge grey area that depends on your style and opinion. There are some things many people will still argue, and if that's the case, then yea, don't bring it into the clean code debate for this repo. (but surely don't use output params for example isn't one that I think needs to be debated much).

I don't know the roadmap of this repo, I just stumbled across it when someone shared it out in the Craftsmanship slack yesterday.

Now I've probably started a fire :). If so, I apologize in advance.

from clean-code-javascript.

dschinkel avatar dschinkel commented on May 2, 2024

I also think having conversations around this repo about coding style is very approrpriate and a really good place to be doing so. After-all, there's a Clean Code forum in Google Forums where people talk about syntax a lot as it relates to Clean Code principals. So I feel people giving inputs in here in comments is just fine and pretty valuable in fact, we can learn a lot here by doing so. Ultimately @ryanmcdermott who stared the repo holds the keys and makes the final decisions which changes get accepted. But conversations should be invited IMO. I've learned a lot from reading comments on various repos over my career for different projects.

Maybe eventually once this repo grows, it might be valuable to show alternate examples of the same things. And put a note "some think this is code smell, just be aware" (and site blog posts or other resources as to why that may be)...but the goal of ultimately showing many ways of doing the same thing in a particular clean code section.

That might make the Readme too complex but just a thought. Maybe there will be ways to organize that kind of stuff if this expands.

from clean-code-javascript.

dschinkel avatar dschinkel commented on May 2, 2024

Great post, thx!

from clean-code-javascript.

tk-o avatar tk-o commented on May 2, 2024

@Tarabass @aaronmcadam I think each programming language has its own clean code way - it mainly depends on features it has. As @ericelliott mentioned JavaScript inheritance finally comes to OLOO (Objects Linking to Other Objects). The class keyword is a syntax sugar and it suggest you can use a classical inheritance in your code, but in fact you can't.

Of course you can still use it as it does its job, although it's not the best option - and since we try here to define proper clean code guide for JavaScript we should promote the best options first.

I believe the inheritance/composition topic is comprehensively covered here:

from clean-code-javascript.

dschinkel avatar dschinkel commented on May 2, 2024

thx for the share @kopacki

from clean-code-javascript.

jokeyrhyme avatar jokeyrhyme commented on May 2, 2024

This, according to Eric Elliott, is object composition and not class inheritance.

@Jeff-Mott-OR none of your code examples in #40 (comment) are what @ericelliott describes as "object composition". To me (at least), if the extends keyword or the prototype property is involved, then we are talking about inheritance, not composition.

from clean-code-javascript.

ryanmcdermott avatar ryanmcdermott commented on May 2, 2024

I echo what @jokeyrhyme says above, let's keep it civil! Very good to hear a thorough discussion about these topics from very experienced developers such as yourselves, we can come to a friendly resolution šŸ˜„

from clean-code-javascript.

Jeff-Mott-OR avatar Jeff-Mott-OR commented on May 2, 2024

none of your code examples in #40 (comment) are what @ericelliott describes as "object composition".

Eric Elliott himself described them as such. Here is a question asked to Elliott, and his reply (plus an imgur mirror of his reply in case it gets deleted, which has happened before).

from clean-code-javascript.

Jeff-Mott-OR avatar Jeff-Mott-OR commented on May 2, 2024

Regardless of the terminology we use, ultimately we're discussing how best to share code between objects in a way that minimises copy-pasting / duplication.

Sure. Just be aware that the paintbrush Eric Elliott advocates to share code is inheritance. At no point did you learn to use object composition (as the GoF meant it when they coined "favor composition"). Nor does Elliott's inheritance-in-disguise solve any of the problems he claims it does. You're still susceptible to the fragile base problem, and you can still get yourself into the banana-gorilla problem if you're not careful.

from clean-code-javascript.

jokeyrhyme avatar jokeyrhyme commented on May 2, 2024

In React, we use "higher-order components". Maybe another way to think of "composition" is "higher-order factories", or "higher-order constructors".

from clean-code-javascript.

ericelliott avatar ericelliott commented on May 2, 2024

@Jeff-Mott-OR I am in this thread, and I can speak for myself. You've already tried to explain my position a couple times, and both times completely missed the major, salient difference between composition and class inheritance (remember, there are several different kinds of inheritance).

So, for the record:

I didn't invent any of these definitions. This stuff has been around in some form or other since before I was born.

Class inheritance creates an is-a relationships, which creates class taxonomies -- essentially an inheritance tree. In JavaScript, this is largely accomplished with prototype delegation -- a form of prototypal inheritance.

Object composition creates has-a or uses-a relationships. Instead of B is A, B has an A or uses an A. In JavaScript, this is largely accomplished with concatenative inheritance (AKA mixins), a form of prototypal inheritance.

The central is-a vs has-a distinction is explained well by -- well hell, just Google this for yourself -- you'll find lots of examples of people saying similar things: "what's the difference between composition and inheritance?"

Note that lots of the answers are about Java, and nobody in the Java world listens to me, so there's no way my influence has snuck in and distorted definitions over there.

People are too lazy to Google, so I'll just quote a few:

"They are absolutely different. Inheritance is an 'is-a' relationship. Composition is a 'has-a'."
source: StackOverflow, top answer, first sentence, "Difference between Inheritance and Composition"

"Make sure inheritance models the is-a relationship My main guiding philosophy is that inheritance should be used only when a subclass is-a superclass. In the example above, an Apple likely is-a Fruit, so I would be inclined to use inheritance. [...] Don't use inheritance just to get code reuse If all you really want is to reuse code and there is no is-a relationship in sight, use composition." - Java World, "Inheritance versus composition: Which one should you choose?"

Composition - has-a relationship between objects.
Inheritance - is-a relationship between classes. - Quora top answer "What is the difference between composition and inheritance in Java?"

What does this mean in practice in JavaScript? For a really nice explanation using JavaScript watch "Composition Over Inheritance".

Composition vs Inheritance Boils Down to Is-A vs Has-A

Is-A vs Has-A/Uses-A is the primary salient difference between class inheritance and object composition. All this dribbling on about techniques like class A extends B vs Object.create() vs Object.assign() is completely, utterly, blindingly beside the point.

Why Not Just Use the Composite Pattern Like the GoF was Talking About?

Wouldn't that be nice? Then we could all stop throwing mud at each other over strict stickler definitions and get some programming done.

It turns out, there's a good reason not to use the composite pattern in JavaScript: It's more complicated than necessary if all you want to do is achieve foo has feature bar. So why do they use it so much more in Java? Because Java has static type classes. Extending objects in Java requires a lot more jumping through hoops. The GoF were Java programmers writing for an audience who mostly understood Java. This is an important bit of historical context that people who misunderstand composition seem to be missing.

In Java, composition is frequently accomplished with the composite pattern or delegate objects because those are the most convenient ways to inherit using feature-based rather than taxonomy based inheritance.

Because JavaScript supports dynamic object extension, concatenation/mixins can accomplish the same goal (added flexibility, through feature-based rather than taxonomy-based inheritance) by extending the target object, instead. This leads to less jumping through hoops, and works for 99% of cases. Once in a while, you want to encapsulate the features that the new object will have and expose a different public API, in which case, the composite pattern / method delegation are fine in JavaScript, too.

Again, to restate my thesis from above, I don't care what techniques people use to accomplish has-a/uses-a relationships instead of is-a relationships. That's completely, absolutely, unequivocally missing the point.

Forget which paintbrush you use to paint with. Look at the painting, instead.

from clean-code-javascript.

dschinkel avatar dschinkel commented on May 2, 2024

I vote to close this issue @ryanmcdermott

from clean-code-javascript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    šŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. šŸ“ŠšŸ“ˆšŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ā¤ļø Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.