Inheritance is the complex idea that one class is a specialization of another class.
727
Inheritance is perhaps the most distinctive attribute of object-oriented
728
programming, and it should be used sparingly and with great caution. A great
729
many of the problems in modern programming arise from overly enthusiastic use
730
of inheritance.
731
The purpose of inheritance is to create simpler code by defining a base class that
732
specifies common elements of two or more derived classes. The common
733
elements can be routine interfaces, implementations, data members, or data
734
types.
735
When you decide to use inheritance, you have to make several decisions:
736
● For each member routine, will the routine be visible to derived classes? Will
737
it have a default implementation? Will the default implementation be
738
overridable?
739
● For each data member (including variables, named constants, enumerations,
740
and so on), will the data member be visible to derived classes?
741
The following subsections explain the ins and outs of making these decisions.
742
Implement “is a” through public inheritance
743
When a programmer decides to create a new class by inheriting from an existing
744
class, that programmer is saying that the new class “is a” more specialized
745
version of the older class. The base class sets expectations about how the derived
746
class will operate (Meyers 1998).
747
If the derived class isn’t going to adhere completely to the same interface 748
contract defined by the base class, inheritance is not the right implementation
749
technique. Consider containment or making a change further up the inheritance
750
hierarchy.
751
The single most important rule in object- oriented programming with C++ is this: public inheritance means “isa.” Commit this rule to memory.
Design and document for inheritance or prohibit it
752
Inheritance adds complexity to a program, and, as such, it is a dangerous
753
technique. As Java guru Joshua Bloch says, “design and document for
754
inheritance, or prohibit it.” If a class isn’t designed to be inherited from, make its
755
members non-virtual in C++, final in Java, or non overridable in Visual Basic so 756
that you can’t inherit from it.
757
Adhere to the Liskov Substitution Principle
758
In one of object-oriented programming’s seminal papers, Barbara Liskov argued
759
that you shouldn’t inherit from a base class unless the derived class truly “is a”
760
more specific version of the base class (Liskov 1988). Andy Hunt and Dave
761
Thomas suggest a good litmus test for this: “Subclasses must be usable through
762
the base class interface without the need for the user to know the difference”
763
(Hunt and Thomas 2000).
764
In other words, all the routines defined in the base class should mean the same
765
thing when they’re used in each of the derived classes.
766
If you have a base class of Account, and derived classes of CheckingAccount, 767
SavingsAccount, and AutoLoanAccount, a programmer should be able to invoke 768
any of the routines derived from Account on any of Account’s subtypes without 769
caring about which subtype a specific account object is.
770
If a program has been written so that the Liskov Substitution Principle is true,
771
inheritance is a powerful tool for reducing complexity because a programmer can
772
focus on the generic attributes of an object without worrying about the details. If,
773
a programmer must be constantly thinking about semantic differences in subclass
774
implementations, then inheritance is increasing complexity rather than reducing
775
it. Suppose a programmer has to think, “If I call the InterestRate() routine on 776
CheckingAccount or SavingsAccount, it returns the interest the bank pays, but if I 777
call InterestRate() on AutoLoanAccount I have to change the sign because it 778
returns the interest the consumer pays to the bank.” According to Liskov, the
779
InterestRate() routine should not be inherited because its semantics aren’t the 780
same for all derived classes.
781
Be sure to inherit only what you want to inherit
782
A derived class can inherit member routine interfaces, implementations, or both.
783
Table 6-1 shows the variations of how routines can be implemented and
784
overridden.
Table 6-1. Variations on inherited routines
786
Overridable Not Overridable
Implementation: Default Provided
Overridable Routine Non-Overridable Routine
Implementation: No default provided
Abstract Overridable Routine
Not used (doesn’t make sense to leave a routine undefined and not allow it to be overridden) As the table suggests, inherited routines come in three basic flavors:
787
● An abstract overridable routine means that the derived class inherits the 788
routine’s interface but not its implementation.
789
● An overridable routine means that the derived class inherits the routine’s 790
interface and a default implementation, and it is allowed to override the
791
default implementation.
792
● A non-overridable routine means that the derived class inherits the routine’s 793
interface and its default implementation, and it is not allowed to override the
794
routine’s implementation.
795
When you choose to implement a new class through inheritance, think through
796
the kind of inheritance you want for each member routine. Beware of inheriting
797
implementation just because you’re inheriting an interface, and beware of
798
inheriting an interface just because you want to inherit an implementation.
799
Don’t “override” a non-overridable member function
800
Both C++ and Java allow a programmer to override a non-overridable member
801
routine—kind of. If a function is private in the base class, a derived class can 802
create a function with the same name. To the programmer reading the code in the
803
derived class, such a function can create confusion because it looks like it should
804
by polymorphic, but it isn’t; it just has the same name. Another way to state this
805
guideline is, Don’t reuse names of non-overridable base-class routines in derived
806
classes.
807
Move common interfaces, data, and behavior as high as possible in the
808
inheritance tree
809
The higher you move interfaces, data, and behavior, the more easily derived
810
classes can use them. How high is too high? Let abstraction be your guide. If 811
you find that moving a routine higher would break the higher object’s
812
abstraction, don’t do it.
813
Be suspicious of classes of which there is only one instance
814
A single instance might indicate that the design confuses objects with classes.
815
Consider whether you could just create an object instead of a new class. Can the
variation of the derived class be represented in data rather than as a distinct
817
class?
818
Be suspicious of base classes of which there is only one derived class
819
When I see a base class that has only one derived class, I suspect that some
820
programmer has been “designing ahead”—trying to anticipate future needs,
821
usually without fully understanding what those future needs are. The best way to
822
prepare for future work is not to design extra layers of base classes that “might
823
be needed someday,” it’s to make current work as clear, straightforward, and
824
simple as possible. That means not creating any more inheritance structure than
825
is absolutely necessary.
826
Be suspicious of classes that override a routine and do nothing inside the
827
derived routine
828
This typically indicates an error in the design of the base class. For instance,
829
suppose you have a class Cat and a routine Scratch() and suppose that you 830
eventually find out that some cats are declawed and can’t scratch. You might be
831
tempted to create a class derived from Cat named ScratchlessCat and override 832
the Scratch() routine to do nothing. There are several problems with this 833
approach:
834
● It violates the abstraction (interface contract) presented in the Cat class by 835
changing the semantics of its interface.
836
● This approach quickly gets out of control when you extend it to other
837
derived classes. What happens when you find a cat without a tail? Or a cat
838
that doesn’t catch mice? Or a cat that doesn’t drink milk? Eventually you’ll
839
end up with derived classes like ScratchlessTaillessMicelessMilklessCat. 840
● Over time, this approach gives rise to code that’s confusing to maintain
841
because the interfaces and behavior of the ancestor classes imply little or
842
nothing about the behavior of their descendents.
843
The place to fix this problem is not in the base class, but in the original Cat class. 844
Create a Claws class and contain that within the Cats class, or build a constructor 845
for the class that includes whether the cat scratches. The root problem was the
846
assumption that all cats scratch, so fix that problem at the source, rather than just
847
bandaging it at the destination.
848
Avoid deep inheritance trees
849
Object oriented programming provides a large number of techniques for
850
managing complexity. But every powerful tool has its hazards, and some object-
851
oriented techniques have a tendency to increase complexity rather than reduce it.
852
In his excellent book Object-Oriented Design Heuristics, Arthur Riel suggests 853
limiting inheritance hierarchies to a maximum of six levels (1996). Riel bases his
recommendation on the “magic number 7±2,” but I think that’s grossly
855
optimistic. In my experience most people have trouble juggling more than two or
856
three levels of inheritance in their brains at once. The “magic number 7±2” is
857
probably better applied as a limit to the total number of subclasses of a base 858
class rather than the number of levels in an inheritance tree.
859
Deep inheritance trees have been found to be significantly associated with
860
increased fault rates (Basili, Briand, and Melo 1996). Anyone who has ever tried
861
to debug a complex inheritance hierarchy knows why.
862
Deep inheritance trees increase complexity, which is exactly the opposite of
863
what inheritance should be used to accomplish. Keep the primary technical
864
mission in mind. Make sure you’re using inheritance to minimize complexity. 865
Prefer inheritance to extensive type checking
866
Frequently repeated case statements sometimes suggest that inheritance might be 867
a better design choice, although this is not always true. Here is a classic example
868
of code that cries out for a more object-oriented approach:
869
C++ Example of a Case Statement That Probably Should be Replaced
870 by Inheritance 871 switch ( shape.type ) { 872 case Shape_Circle: 873 shape.DrawCircle(); 874 break; 875 case Shape_Square: 876 shape.DrawSquare(); 877 break; 878 ... 879 } 880
In this example, the calls to shape.DrawCircle() and shape.DrawSquare() should 881
be replaced by a single routine named shape.Draw(), which can be called 882
regardless of whether the shape is a circle or a square.
883
On the other hand, sometimes case statements are used to separate truly different 884
kinds of objects or behavior. Here is an example of a case statement that is 885
appropriate in an object-oriented program:
886
C++ Example of a Case Statement That Probably Should not be
887 Replaced by Inheritance 888 switch ( ui.Command() ) { 889 case Command_OpenFile: 890 OpenFile(); 891 break; 892
case Command_Print: 893 Print(); 894 break; 895 case Command_Save: 896 Save(); 897 break; 898 case Command_Exit: 899 ShutDown(); 900 break; 901 ... 902 } 903
In this case, it would be possible to create a base class with derived classes and a
904
polymorphic DoCommand() routine for each command. But the meaning of 905
DoCommand() would be so diluted as to be meaningless, and the case statement 906
is the more understandable solution.
907
Avoid using a base class’s protected data in a derived class (or make that
908
data private instead of protected in the first place)
909
As Joshua Bloch says, “Inheritance breaks encapsulation” (2001). When you
910
inherit from an object, you obtain privileged access to that object’s protected
911
routines and data. If the derived class really needs access to the base class’s
912
attributes, provide protected accessor functions instead.
913