The compulsion to introduce unnecessary distinctions between objects leading to an overcomplicated class hierarchy.
Major indicator: classes that add nothing new.
To paraphrase BM's taxomania rule:
- Every descendant class must introduce new functionality, constraints or attributes.
This seems overly generous to me. You can still create bad designs which cause CombinatorialExplosion
in terms of distinctions between classes. OO modelling is about essential
structure. A huge inheritance hierarchy introducing only trivial differences at each stage reeks of modelling at the wrong level of abstraction.
BM's example of the multiple inheritance on page 544 (the FrenchUSDriver class) is an example of such a combinatorial explosion. (The class inherits Driver from both FrenchDriver?
and USDriver.) What about the UKFrenchUSPakistaniItalianDriver class, and is that the same as FrenchUKPakistaniItalianDriver?
Split this example into two distinct roles here, Person
(with suitable single country subclasses) and the multiple inheritance problems go away. Shared attributes used to be shared because they belonged to the Person
role (e.g. "name", "dob"...). Attributes remaining in Licence will need a separate instance per country.
Associate each Person with n
Licenses. This association may well be managed by a separate class. You can even make the association 1 person to 0..1 license, qualified by ISO country code.
Common ways of refactoring such models to reduce class proliferation:
- introducing TypeObject (i.e. going meta to allow new types to be defined at runtime.)
- various patterns that make properties explicit (Ref something by BrianFoote I believe?)
- using Composite (explicit recognition of similar structure in classification)
The application of inheritance here isn't even appropriate: the lifetimes of Person
will vary. The original account doesn't even allow representation of a Person who can accumulate or lose licenses over time.
I think that the original example introduces a whole raft of new classes that add the same kinds of data
and the same kinds of functionality
, yet the model doesn't reach closure: you must keep adding classes for each possible licensing permutation.
There is no meaningful distinction between many of the class permutations that can be introduced: (e.g. UKFrenchUSPakistaniItalianDriver and FrenchUKPakistaniItalianDriver). Despite it's size and complexity, the object model doesn't express all relevant conceptual structures in the domain.
It reeks of a classification mania.
Similar issues are addressed in LimitsOfHierarchies. I think that perhaps there is no true, safe IS-A in almost everything encountered in software engineering. Whether hierarchies are a ConvenientApproximation? or not is another issue.
If I were a cynic ;-) I'd say that this problem has been contrived to bring about a multiple inheritance "crisis" to make Eiffel feature renaming look good. There's no crisis about which attributes need to be shared and which are copied if you just identify the right classes in the first place.
A better example of the need for multiple inheritance is a robust implementation of a new class, "Matrix". Instances of Matrix must provide their own specialized behavior for an interesting subset of arithmetic operations, and must also provide appropriate collection behavior. Thus, in a Smalltalk context, they want to be descendents of both Collection and Magnitude. In my opinion, MultipleInheritance is the most concise and robust way to model this behavior of Matrix. Once the nose of MultipleInheritance is permitted inside the tent, the rest of the camel must surely follow, leading to the need for some mechanism to address conflicts about operations and behavior among ancestors. -- TomStambaugh
Agreed, once the nose of MultipleInheritance
is in the tent, you have to handle it properly. Having said that, sometimes you just have to acknowledge that you've got a "has-a" rather than an "is-a" relationship. --