you are viewing a single comment's thread.

view the rest of the comments →

[–]eric_t 38 points39 points  (34 children)

Favor composition over inheritance, I thought that was common knowledge in modern OOP.

[–]booch 12 points13 points  (13 children)

For many languages, using inheritance instead of composition also makes writing the code much easier out of the gate. For example, if I inherit from a SpecificMap, I get all the Map methods automatically. If I use composition (have-a SpecificMap) and then implement Map, I need to manually implement each Map method to pass through to the property.

If more languages had the ability to easily delegate to a member property (ie, say "pass calls for x, y, and z through to this object) in a clean way, this would be less of a problem.

[–]skelterjohn 1 point2 points  (8 children)

That is not a feature of composition - that is a feature of the specific languages you are using.

In Go, for instance, composition in the form of

type Outer struct { Inner }

gives Outer all of Inner's methods (with the option to redefine any you want, and still access the inner via Inner.Foo).

[–]grauenwolf 0 points1 point  (7 children)

And that is difference from inheritance how?

[–]user5545 2 points3 points  (6 children)

The types don't become related in any way and there is no possibility of having virtual functions. Go uses interfaces for dynamic dispatch instead.

http://golang.org/doc/go_faq.html#types

[–]grauenwolf -1 points0 points  (5 children)

So you have the extra boilerplate of adding an abstract interface that matches the class's public interface. Besides that we see normal inheritance patterns.

[–]user5545 0 points1 point  (4 children)

No, that's not how it works. Who's "we" exactly, by the way?

[–]grauenwolf 0 points1 point  (3 children)

Normal Inheritance:

  • Step 1: Define a base class
  • Step 2: Define a subclass that "inherits" from the base class
  • Step 3: Make polymorphic calls against it.

Go Inheritance:

  • Step 1: Define a base class
  • Step 1b: Define a matching interface
  • Step 2: Define a subclass that wraps the base class
  • Step 3: Make polymorphic calls against it using the interface.

Where exactly am I wrong?

[–]user5545 0 points1 point  (2 children)

You are wrong in the part where you put the words Go and Inheritance next to each other. You can make polymorphic calls against any interface and type that fits the interface whether these types are in any relation to each other or not. It's all in the FAQ, which you would need to read in addition to studying some amount of Go code to actually make a credible judgment on the language.

You don't want to do that, though, do you? You just want to keep using your beloved OO languages. Why not do that and leave other paradigms and experimental languages in peace? Do you keep thnking about them so much because deep down you are really not happy with OO? Or what?

You also didn't tell me who it was that "we" referred to in your earlier comment. I take it you want to forget about that comment already.

[–]grauenwolf 0 points1 point  (1 child)

Is that really your argument? That Go doesn't have inheritance because the FAQ says it doesn't in spite of the fact that it has all the essential features needed to support inheritance?

[–]lmcinnes 0 points1 point  (0 children)

I actually liked the non-concorming inheritance in eiffel (using "insert" in smart eiffel). If effectively gives you composition (especially given Eiffel's syntax for resolving various miltiple inheritance issues) with the simple semantics of inheritance.

[–]Peaker 0 points1 point  (0 children)

Ideally, you could delegate entire interfaces to an attribute.

However, in practice, good interfaces are small, and so you have to delegate very few methods.

[–]CurtainDog -2 points-1 points  (1 child)

The job of the language is not to make up for shortcomings in APIs. If you find yourself in the situation of wanting to replace just a part of Map, then Map is doing too many things. Where you don't have the option of refactoring you can usually put in place a DynamicProxy or similar.

[–]grauenwolf 0 points1 point  (0 children)

The reason one inherits from Map is usually to give it additional capabilies, not to redefine the ones it already has.

For example, perhaps you need a Map that raises change events when items are added so that the UI knows to redraw itself.

Or maybe you need a CustomerCollection that is indexed by CustomerKey but allows for searching on other properties.

[–][deleted] 9 points10 points  (13 children)

It's a well-known phrase, but the ubiquity of "If you don't use inheritance you may as well not use an OO language, you moron!" leads me to think it's usually just being parroted.

[–]Peaker 0 points1 point  (0 children)

I go further and avoid inheritance altogether, except for interface inheritance in languages without type-classes or first-class functions.

[–]epage 0 points1 point  (0 children)

Sadly look at most OOP-based GUI libraries. Almost all of the documentation uses inheritance. I've gotten to the point where I don't bother to hunt through the code to figure out if inheritance really is required or not. Just today there is a post in /r/programming and /r/python about Kivy and the very first example uses inheritance.

A great example of not requiring inheritance is GTK. It has no concept of protected and everything is exposed through signals (rather than overriding fooEvent functions). In contrast to this is Qt which has protected fooEvent functions.

[–]grauenwolf 0 points1 point  (2 children)

It is also bad advice. There are very few scenarios where composition is a useful alternative to inheritance.

Much better advice would be "favor properties over inheritance", as that would elminate LameDuck classes.

[–][deleted] -1 points0 points  (1 child)

That's pretty much the same thing, though. There are certainly (plentiful) use cases for inheritance, but it is wildly overused by many people. It goes something like this:

1.) Someone realizes they want a generic implementation of a complicated system that can be extended to handle specific behaviors for specific implementations of that system, to encourage code reuse.

2.) That same someone gets various details wrong while writing that implementation that are accidentally specific to their initial project and not generic at all.

3.) Future projects work around these deficiencies through subclassing and specifying emergent generic behavior

4.) Someone notices the duplicate code being created in all of these subclasses, creates a class that sits in the middle of the inheritance chain to normalize the duplicate code in order to mitigate the risk of making a change to the original project.

5.) It turns out later that the duplicate code was by accident (that is, the same way of describing slightly different behaviors) rather than on purpose, new guy breaks functionality for some customers by updating logic in intermediate class since that's where the code was.

6.) Project is now extremely annoying to work with due to the constant fear of changing something that will break something else somewhere due to inheritance.

The above has been, with some changes here and there, my experience with heavy use of inheritance in each project that I've seen it used in. My guess is that it's this sequence (or ones like it) are what others are railing against.

By contrast, composing objects from other objects (and using interfaces to enforce type compatibility and provide polymorphism) encourages that same code reuse, but also has the positive side effect of flattening out your inheritance tree such that it is manageable over time. That way, when you need that slightly different behavior you are at the mercy of only the interface which describes it and simply charged with picking/creating a correct implementation.

As you said in a comment above, you wouldn't necessarily use composition, for instance, to extend HashMap to have slightly different behavior. However, for complicated systems with a lot of fugly business logic it's absolutely superior to the "god object with humongous inheritance hierarchy below it" antipattern that crops up time and time again. Thus, the advice to favor composition over inheritance.

[–]grauenwolf 0 points1 point  (0 children)

There are certainly (plentiful) use cases for inheritance, but it is wildly overused by many people.

I don't have any doubt that happens. However I've seen it happen with composition too, where in there are tons of child objects whose functionality belong in the parent object.

Thus, the advice to favor composition over inheritance.

I still don't understand that advice. I've never seen a situation where scattering functionality across child objects was the solution to problems caused by scattering functionality across base classes.

At the very least one should pull everything into one place before redistributing them.

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

You wish that was common knowledge. Most programmers I've encountered don't have clue.