Avoid concrete base classes

From CSSEMediaWiki
Jump to: navigation, search
All base classes should be abstract classes. --Riel's Heuristic 5.7, Arthur Riel 1996

Riel derived this heuristic from Johnson and Foote 1988: The top of the class hierarchy should be abstract. He states that all base classes of an inheritance hierarchy should be abstract. Note that this does not mean that single classes with no inheritance hierarchy should be made abstract, as this would mean a concrete subclass would have to be created that adds no meaningful functionality - this violates Heuristic 3.7; Eliminate irrelevant classes.

This heuristic is a trade off between design complexity and flexibility. The example Riel gives (taken from the Object Oriented Design Heuristics book) is a FullEmployee class that inherits from a concrete NewEmployee class. It seems to tie in with Avoid inheritance for implementation. In this case we cannot add something to the NewEmployee class without it also being added (via inheritance) to the FullEmployee class.

5.7 example1.png

This problem can be avoided by recognising that a NewEmployee and a FullEmployee have something in common, which can be represented by an abstract base class that they both inherit from. The new design would be:

5.7 example2.png

The worst effect of violating this heuristic is that the name of the original concrete base class must be globally changed to the new abstract base class. This usually cannot be avoided as the name of the concrete base class is too specific for the abstract base class.

Abstract base classes should not always be used, however. The case when not to use an abstract base class is described in Heuristic 3.7, Eliminate irrelevant classes. Do not create an abstract base class if one of the concrete subclasses does not add any meaningful functionality, ie it would be an empty class with only inherited attributes. It can be hard to find a good balance between these two conflicting heuristics. Riel recommends finding the classes that are most likely to change, and to add irrelevant subclasses for these, leaving the other classes that are unlikely to change as inheriting from a concrete class. This minimizes the effect of having too many irrelevant classes in your design, and having too many potential global name changes for classes.

Criticism of Riel's reasoning

Riel's example as to why concrete base classes are bad (the one shown above) is unconvincing. The initial design has another issue which is causing problems - the inheritance is just plain wrong. A FullEmployee should most certainly not inherit from a NewEmployee - this is a blatant violation of the Liskov substitution principle. It's not even really a case of Avoid inheritance for implementation. The simple fact of the matter is that FullEmployee does not have an *is-a* relationship with NewEmployee. The design also seems to have a Becomes problem, since an employee is not presumably new forever.

Riel argues that it's a problem, because we can't add something to the concrete superclass X without it also being added to the subclass Y. However, there's no reason why this should be a problem, assuming that Y actually *is-a* X and doesn't violate the Liskov substitution principle.

Conclusion: Riel's reasoning is flawed. If anyone has a real (ha ha) example of why concrete base classes are a bad idea, we'd like to hear about it.

See also

Personal tools