Hacker News new | ask | show | jobs
by tigershark 3625 days ago
The purpose is completely different. You use interfaces and composition to decouple the implementation of some objects, you use inheritance to define some object hierarchy. Inheritance specifies the is a relationship, Composition the has a. As you can see these two are totally different concepts.
1 comments

What is an is a relationship? It is the promise that a type implements a contract. That sounds like an interface to me; implementing interfaces also forms is a relationships. Allow interfaces to be externally defined for a type, and now you have Haskell classes or Rust traits. This isn't a new idea I'm proposing here; it's quite workable and very straight-forward.

EDIT: To be clear, in inheritance, the contract being implemented is the implicit contract specified by the parent type. (Assuming you're following the Liskov substitution principle, which you should be.)

you were not speaking about interfaces, you were speaking about composition. As I said composition and inheritance are two completely different concepts. And I'm not making up the definitions, is a and has a are very well known concepts in computer science. You can argue ad libitum, but this doesn't change the definition. https://en.m.wikipedia.org/wiki/Has-a https://en.m.wikipedia.org/wiki/Is-a
I'm starting to feel like you're not actually reading anything I write. I specifically include interfaces. To quote myself:

> Also, maybe eliminate inheritance and force interfaces and composition instead.

Implementing an interface meets all the conditions of an is a relationship; no redefinition is necessary. [0] After all, interfaces are nothing more than abstract base classes with no method bodies to inherit. I haven't argued anywhere that composition defines an is a relationship, so I have no idea where you're pulling that from.

[0] https://en.m.wikipedia.org/wiki/Is-a#Java

EDIT: I really like this article you linked. It has all sorts of things that help me prove my point. For instance, the Rectangle example from the LSP section. If Rectangle were an interface:

    interface IRectangle {
        double getWidth();
        double getHeight();
    }
It's trivial to implement concrete Rectangle and Square classes that don't violate any principles. Inheritance, on the other hand, is pretty much guaranteed to be broken as long as Rectangle is mutable, because Square cannot add additional constraints to Rectangle and still pretend to be a Rectangle in all use-cases.
You wrote this: "Inheritance is nothing but composition with some default implicit virtual method routing." In response to my post where I said that inheritance and composition are two different tools and doesn't make sense to say that one is always better than the other. So it seems to me that you think that inheritance and composition are the same concepts given your words that I quoted and given that you were arguing with my definition of different tools.

Edit: I don't see any composition in your example, just an interface implementation.

OK, I should have stated that inheritance is a bundling of multiple tools, composition and default implicit virtual method routing being two of them. It also creates an is a relationship, which in my version of an inheritance-less world would be handled explicitly via interfaces, which I included in my original "toolbox".

EDIT: Notice how I can enforce the invariants of `Square`, while composing a `Rectangle` and implementing `IRectangle`. This works because composition allows me to encapsulate the problematic setter methods of `Rectangle`, which is not something I can do if I inherit it.

    class Square implements IRectangle {
        private final Rectangle rectangle;

        public Square(final double size) {
            this.rectangle = new Rectangle(size, size);
        }

        public double getWidth() {
            return this.rectangle.getWidth();
        }

        public double getHeight() {
            return this.rectangle.getHeight();
        }

        public double getSize() {
            return this.rectangle.getWidth();
        }

        public double setSize(final double size) {
            this.rectangle.setWidth(size);
            this.rectangle.setHeight(size);
        }
    }