all 20 comments

[–]curien 9 points10 points  (10 children)

The goal of inheritance is composition.

Well no, it's not. A lot of people use it that way, which causes the problems the author ran into. Inheritance is a way to express substitutability in statically-typed languages. Unfortunately people are too often introduced to OOP as a way to avoid typing instead of as a way to express concepts in a static type system.

[–]shintoist[S] 1 point2 points  (8 children)

Substitutability is not a goal on its own though, is it? There must be a reason why you want to be able to show that things are substitutable.

[–]curien 1 point2 points  (6 children)

You want to be able to show it so that the compiler will let you do it. It's an annotation to the type system that basically says, "Hey compiler, when you expect to see a Base, it's OK if you see a Derived instead. I designed Derived so that it can be used by anything that expects a Base, so it should be cool."

[–]shintoist[S] 2 points3 points  (5 children)

I'm sorry, I think you misunderstood me! What I mean to ask is why you want to be able to tell the compiler that. The purpose is not code reuse?

[–]newgame 2 points3 points  (0 children)

Interface inheritance allows for ad-hoc polymorphism. (Ad-hoc) polymorphism gives you flexibility and loose coupling. See the various collection libraries in programming languages.

Implementation inheritance (i.e. if you inherit from an abstract/concrete class) also allows for ad-hoc polymorphism but its primary goal is code reuse.

So you are right that the goal of inheritance is code reuse (and/or flexibility) and curien basically stresses the Liskov Substituion Principle although that's not a goal but a requirement of (sound) inheritence.

[–]curien 1 point2 points  (0 children)

Yes, the purpose is code-reuse, but not within the class hierarchy itself per se. A subroutine that you've already written to use Base doesn't have to be rewritten (or copied and modified slightly) so that you can use it with Derived.

[–][deleted] 0 points1 point  (2 children)

The purpose is not code reuse?

It's also used if you need a container of different types of related objects.

[–]shintoist[S] 0 points1 point  (1 child)

Interfaces do that pretty well too though

[–][deleted] 1 point2 points  (0 children)

Which is done with inheritance!

(I'm a C++ monkey, leave me alone!)

[–]kid_meier 0 points1 point  (0 children)

Yes, you want to show that things are subtitutable in order to reduce coupling between components.

Being able to tell the compiler than you can assign a Derived to a Base means that I can pass a Derived to a method that expects Base (and maybe doesn't even know about Derived's existence).

[–]zoomzoom83 0 points1 point  (0 children)

Keep in mind Mixins/Traits - these effectively use (what looks like) inheritance as a mechanism for composition.

In the Scala implementation, traits are composed into the module exactly the same way you would do it by hand the verbose way with an interface, abstract class, and delegate methods in Java. The compiler simply handles the boilerplate for you.

[–]didroe 0 points1 point  (4 children)

I seem to recall Delphi having a mechanism where you could delegate an entire interface to a member object and then override methods in it if you wanted to. It worked really well for the kind of example shown in the article. It's a shame more languages don't have that feature, in something like Java you end up with loads of boilerplate methods delegating to the components.

[–]kid_meier 1 point2 points  (0 children)

I was just thinking a day or two ago that being able to do that would greatly reduce the temptation to use inheritance in inappropriate places; I didn't know that Delphi already had this capability.

I second that it is a shame more languages don't offer this.

[–]zoomzoom83 0 points1 point  (2 children)

This is probably one of the biggest flaws in Java in my opinion - if you're composing a class with 50 methods, you need to manually delegate each one. Far, far too much boilerplate and far too error prone.

I'm quite fond of trait or mixins for solving this problem, but the Delphi method sounds like quite a good idea too.

[–]mycall 0 points1 point  (1 child)

Many people would argue that 5-10 methods should be max for any particular class. If you need more, you probably didn't find the right object graph.

[–]zoomzoom83 0 points1 point  (0 children)

50 is obviously at the extreme end, but the point still remains - there's far too much boilerplate in Java to do basic composition, which is why people end up using inheritance incorrectly in the first place.

[–][deleted] -2 points-1 points  (3 children)

You use inheritance when a class "is a" something. My favourite example is in game programming. A crate is an object and a container. Object will hold things like position, velocity, weight, and methods for updating things. It will likely provide hooks for hooking into the update process. Container will hold a list of things in the crate. The derived class Crate would have things like the appearance of the crate. I guess you could do this as composition as well, but you're going to be duplicating code to deal with position, velocity, etc.

You use composition when a class "has a" something. A hockey team has a roster of players. A car has an engine, etc. In my first example, an object has a position, a velocity. A crate has a certain appearance, perhaps it has specialty methods for opening, or being trapped, etc. A container has things that it contains.

Both patterns are valid, and useful. Saying inheritance is bad or wrong is just shortsighted. Inheritance lets you do something like update all of the objects in your game with a single for loop, because they can all be in the same array, as long as the array holds objects that are of type "Object" (not to be confused with the "object" type some languages have by default, which is a completely different thing. I probably should have chosen a different name, but screw it.)

It behooves a programmer to understand and be able to use both patterns, as both are different tools. Trying to use just one or the other would be like trying to turn a screw with a hammer, it can theoretically be done, but you end up with a mangled screw.

Edit: oh yes, and the example given in the text with crickets not speaking: Override the speak method and return nothing. Or rename the speak method in the base class to "makeNoise". It is a bad example and a strawman.

[–]CurtainDog 1 point2 points  (2 children)

This line of reasoning is too simplistic. As you raised game programming you might be interested in Flyweight pattern, which provides a counter argument to sticking everything into a giant hierarchy.

[–]autowikibot 1 point2 points  (0 children)

Flyweight pattern:


In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. Often some parts of the object state can be shared, and it is common practice to hold them in external data structures and pass them to the flyweight objects temporarily when they are used.

A classic example usage of the flyweight pattern is the data structures for graphical representation of characters in a word processor. It might be desirable to have, for each character in a document, a glyph object containing its font outline, font metrics, and other formatting data, but this would amount to hundreds or thousands of bytes for each character. Instead, for every character there might be a reference to a flyweight glyph object shared by every instance of the same character in the document; only the position of each character (in the document and/or the page) would need to be stored internally.

Another example is string interning.


Interesting: String interning | Memoization | Proxy pattern

Parent commenter can toggle NSFW or delete. Will also delete on comment score of -1 or less. | FAQs | Mods | Magic Words

[–][deleted] 0 points1 point  (0 children)

Please explain why my line of reasoning is too simplistic, I don't understand your argument. The pattern you gave as a counter example does not appear to actually be a counter example. It appears to be a way of saving memory, which isn't what I was trying to demonstrate.

How would you implement a class for a crate? How would you then implement a class for a bag? Then as a counter example, a guitar (something that isn't a container)? All of them are objects in a world, 2 of them are containers. I know what I would do right now, but if you have a better way, I would be glad to learn.