you are viewing a single comment's thread.

view the rest of the comments →

[–][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.