Over on IsComputerScience, Aamod Sane makes a claim about the desirability of shallow hierarchies and gives a number of plausible reasons for why shallow hierarchies are preferable to deep ones (E.g. his argument has FaceValidity?).
But, I wonder about this. What are the general construction principles for hierarchies. I've noticed several environmental factors that influence hierarchy design. For example considering programmers:
My own preference is for deep hierarchies wherein each layer adds a little bit of functionality. There's something deeply and profoundly pleasing about having the inheritance hierarchy reflect the increasing level of specialization of objects. That is, I really like having "Terrier" between "Dog" and "Yorkshire Terrier" even if it adds very little in terms of functionality.
It's hard to separate out language-specific issues, but we should try. Firstly, you've identified strong typing as a factor, where you probably mean manifest typing. Having to keyboard explicit type declarations all over the place is a pain, and inheritance of type is a way of reusing them. A language with static type inferrance could be different.
Currently languages use an explicit sub-type hierarchy. Java separates it from the subclass hierarchy (ie interfaces), but its still explicit. If we had static type checking but implicit type conformance, that would reduce another need for deep type hierarchies.
Another issue is that people traditionally specify the interface between client and server classes much more than between base class and derived class. For example, Eiffel allows derived classes to access all of the base class's implementation; there is no "private" keyword. This is the main reason why a large aggregation system appears easier to manage than a large subclassing system.
Then as you say, not having language support for delegation makes that more laborous.
For ShallowHierarchies to be a pattern I'd agree with, these factors need to be included in its context. -- DaveHarris
I think that deep hierarchies make code much harder to understand. Inevitably, in your method/member function implementations, you use many services (and maybe even data members, depending on language and style) from base classes. When reading a function and trying to comprehend it, one looks for variables locally, then as class members of the local class. Similarly for functions. You look for function calls as member function of the current class, then maybe local statics (I'm thinking mostly C++-flavor here) in and as base class members or global functions. Calls that are obviously member function calls on a specific object can be more easily traced by looking for the object definition first, then at that object's class, then at its base classes. The deeper your hierarchy, the harder it tends to be to find this stuff. I've spent a lot of time trying to grok systems designed with deep hierarchies, and it's frustrating. -- DanMuller
See also DeepClassHierarchies CategoryHierarchy