|< Free Open Study >|
6.2 The Common Misuse of Multiple Inheritance
Since multiple inheritance misuse is so prevalent in object-oriented designs, it makes sense to investigate the reasons for this misuse and see why it is so dangerous. Let us assume that we have three classes: Wings, Fuselage, and Cockpit. We would like to construct an Airplane class from these three classes. We can accomplish the design using two different object-oriented constructs. We could have the Airplane class inherit from Wings, Fuselage, and Cockpit, or it could contain them. Since wings are made of aluminum and rivets, we can extrapolate our design to include them (see Figure 6.1).
Which method is better? Many designers will say that the use of multiple inheritance is wrong because Airplanes are not special types of Fuselages. So what? You took my word for it when I stated that inheritance should always model a specialization hierarchy. Why is it important to do this? Let us examine the data of the two Airplanes. What is the difference between the two designs? It turns out that there is no difference in typical language implementations of multiple inheritance. The only difference in C++ is that the system names the Wings, Fuselage, and Cockpit objects in the first design and the developer names them in the second design. If the data is the same, then the difference must be in the behavior of the Airplane. In the second design, what can an Airplane do, that is, to which messages can it respond? If you guess fly and cost, then consider that rivets know how to heat themselves. Now what can an Airplane do? Of course, the Airplane does not get the heat message in the second design. Why? Because containing an object of a class does not imply that the interface of the containing class gets the interface of the contained class. This is an important characteristic of the containment relationship. The second design is a black-box design. You, the user of an Airplane, do not care how I, the implementor of the Airplane, created the internal design. It makes no difference to you if I broke down an Airplane into a narrow and deep hierarchy as it is pictured above or whether I designed my Airplane as a broad and shallow class containing 6,000 data members like needle gauge covers, oxygen mask cups, oxygen mask hoses, oxygen mask release levers, needle gauge needles, Naugahyde, seat belt buckles, seat belt clasps, etc. So long as the Airplane flies and computes its cost, the users of Airplane are happy. They do not care how the job is accomplished.
To which messages can the Airplane of the first design respond? This is the fundamental problem with using multiple inheritance to simulate containment. The user of the multiply inheriting derived class (i.e., the Airplane) must know how it was implemented in order to use it. This is an unnatural relationship and implies that the Airplane can perform any function that each of its pieces is able to perform. Inheritance is intrinsically a white-box design construct, while containment is intrinsically a black-box design construct. Whenever given the choice between white- and black-box design, always choose the black-box construct. The C++ developers who claimed that multiple inheritance was ruining their designs and their chances for creating reusable components were misusing multiple inheritance by using it in place of the containment relationship. If I thought multiple inheritance was necessary in a language for the implementation of the containment relationship, then I would have been screaming for C++ 2.0 as well.
These two questions will get you out of trouble most of the time. The answers will be yes/no or no/yes, respectively. A yes to the first question and no to the second implies you need inheritance. The opposite answers imply that containment is the better choice. Watch out for accidental multiple inheritance. Consider the following statements checking for legal multiple inheritance.
Is this a valid example of multiple inheritance? No, because citrus fruit is a special type of food (assuming there do not exist inedible citrus fruit). Watch out for accidentally detecting the transitivity of the inheritance relationship and trying to model it as multiple inheritance (see Figure 6.2). Always ask yourself a third question, "Are any of my base classes derived classes of the other base classes?"
Hierarchies that violate this heuristic have what is called accidental multiple inheritance.
|< Free Open Study >|