As you define classes, you may notice that some classes have the same attributes or the same operations. When this is the case, you place these common features (attributes, operations, and so on) in a more generic class called the superclass. The classes that share the common features are known as subclasses of the superclass. For example, the length of recorded material on a videotape, audiotape, compact disc, or movie film is an attribute of all four kinds of recorded media. These classes can share other attributes as well, such as their physical dimensions and the date each one was used to make a recording. In this case the superclass would be RecordedMedia, the subclasses would be Videotape, Audiotape, CompactDisc, and MovieFilm, and some shared attributes could include recordedLength and totalLength.
This process of finding similar attributes or operations across classes is known as generalization. For example you generalize the attribute recordLength into a more generic class called RecordedMedia. The process for showing a generalization in UML is simple:
Identify the subclasses.
Locate classes that have the same attributes and/or operations. These classes are your subclasses.
Create a superclass.
Provide a superclass to hold the common attributes and/or operations of the subclasses. Give the superclass a name that categorizes all the subclasses. We recommend placing the superclass above the subclasses in the diagram. (You don’t have to, but it does make it easier to read.)
Add common features to the superclass.
Remove the common attributes and operations from the subclasses and place them (once) in the superclass.
Draw a generalization relationship.
You draw a generalization line from each subclass to the superclass. In UML the generalization line is represented as a solid line with a hollow arrowhead at the superclass end. In UML, a line with the hollow arrowhead that connects a subclass to a superclass is known as a generalization relationship.
After you create a superclass with the common features such as attributes and operations, the subclasses inherit those features from the superclass. This way you only have to write the common features once in the superclass instead of many times in each of the subclasses.
Tip You can tell whether you have a generalization by looking at the language you (or others) use to describe the relationship between classes. Notice that in describing recorded media and its various types such as videotape earlier in this section, we used the phrase “four kinds of recorded media.” If you find yourself using phrases such as “kind of” or “type of,” then chances are you have a generalization on your hands.
One of our clients is concerned with keeping track of materials in an archive. This client has accumulated different kinds of recorded media such as videotapes and audiotapes. As modelers, we need to capture the differences between these media as well as their similarities. The diagram in Figure 6-1 shows the beginnings of several generalizations, arranged in an inheritance hierarchy.
Figure 6-1: Simple inheritance hierarchy.
Remember Developers use the term generalization or inheritance to refer to the same concept of reusing shared attributes and operations that you show in a superclass and reuse in subclasses. Generalization refers to the concept of generalizing from specifics (the subclasses) to the generic (the superclass). Inheritance refers to the effect of generalization on the subclasses.
In Figure 6-1 RecordedMedia is the superclass. The hollow arrowhead is just below (and right up against) the superclass. Lines from the arrowhead indicate that Videotape, Audiotape, CompactDisc and MovieFilm are all subclasses or “kinds of” RecordedMedia. Each subclass inherits the common attributes of recordedLength, totalLength, height, width, depth, and form. Each of the subclasses also has the operation recommendPlaybackMachine as an inherited common feature from the superclass. Each subclass has its own attributes as well. For example, CompactDisc has two unique attributes (recordedTracks and errorRate) that the other classes don’t share.
Warning When you see a generalization relationship between classes, its meaning is very different from that of an association relationship between classes (as discussed in Chapter 4). An association is ultimately a relationship among many objects—some instances of one class have a relationship (link) with instances of the other class. In a generalization relationship among classes, the relationship is really about the classes. The best you can say is that an object created from a subclass contains all the features of the subclass and of the superclass.
You only have one object from a class in a generalization relationship. Even though you show two classes, the subclass and the superclass, you only have one object that gets created. You can think of an object of the Videotape class also being an object of the RecordedMedia class because of inheritance. Figure 6-2 shows an object created from the Videotape class with all its attributes. (The instance of a class is represented as an object symbol.) You don’t have two different objects (one for RecordedMedia and one for Videotape), just one object. When the object vtu83-1023 was created, we set all its attributes’ values. The recording on the tape is 57 minutes. The total length of the physical tape is 60 minutes. The tape is a Umatic videocassette with a height of 10 inches, a width of 7 inches, and a depth of 1.5 inches. The recording is analog, and a log of tape contents is attached to the tape for the archivist to reference.
Figure 6-2: An instance showing all inherited attributes.
Remember You only have one instance defined by a subclass and its superclass. The subclass and the superclass may have a constructor operation (to create the instance) and a destructor operation (to destroy the instance). When your software runs, and you create an instance of a subclass, the constructor of the superclass is executed first, followed by the constructor of the subclass. When it comes time to eliminate the instance you created, the destructor of the subclass is called first, followed by the destructor of the superclass. If things are more complex because you have subclasses of subclasses, just remember: Constructors are invoked from the top of the inheritance hierarchy to the bottom; destructors are called in order from the lowest subclass up to the highest superclass.