Previous Section  < Free Open Study >  Next Section

5.8 A Real-World Example of Generalization

We consider the preceding example to be specialization because we started with a base class and added a new derived class to our system. Consider the three classes displayed in Figure 5.14. It is obvious that they have some things in common. First, all three contain a weight and a color. This in itself is not enough to constitute an inheritance relationship. If two or more classes share only common data, that is, no common messages, then that common data should be encapsulated in some new class. The two (or more) classes that share the common data can each contain the new class. Since the object-oriented paradigm encapsulates data and behavior in a bidirectional relationship, common data usually implies common behavior. In these cases, an inheritance relationship is required to capture the common abstraction. In our example in Figure 5.14, each of the three classes also has a print and cost function (i.e., common messages). Upon further inspection, we find that the apple and orange classes also have the same implementation for the print function (i.e., common methods). The result is the need to create a common base class called Fruit to capture the common abstractions (see Figure 5.15).

Figure 5.14. Finding commonality among classes.


Figure 5.15. Generalization of Apple, Banana, and Orange to Fruit.


Since we started with derived classes and then found the base class, this is called generalization. While this real-world example makes it look easy to generalize, in practice many of the common abstractions are not found until the late stages of software development. This is not a serious problem since new base classes can be added to the system very late in development with little impact on the system. In this example, even if there were many users of Apple, Orange, and Banana, we could easily add the abstract Fruit class with no effect on these users. The users of these classes are sending messages to apples. They do not care if apple defines the method directly or steals it from a base class. As long as apple prints itself when it is told, the user is happy. Of course, in these situations there is a chance that the implementor of Apple named the method print() but the implementor of Orange named the equivalent method write().

Heuristic 5.8

Factor the commonality of data, behavior, and/or interface as high as possible in the inheritance hierarchy.

The point of this heuristic is to allow as many derived classes as possible to take advantage of a common abstraction. A violation of this heuristic implies that a particular abstraction will need to be redesigned and implemented in each of the derived classes, rather than once in the base class [9]. It is important that all derived classes in the hierarchy share the common abstraction, since the abstraction cannot (or should not) be eliminated by a derived class. See Section 5.17 for details of this common pitfall.

The heuristic also allows users of a derived class to decouple themselves from that class in favor of a more general class. For example, rather than depending on the Apple class, a user could depend on some fruit class, which may be an apple.

    Previous Section  < Free Open Study >  Next Section