Generalization

Generalization

A typical example of generalization involves the personal and corporate customers of a business. They have differences but also many similarities. The similarities can be placed in a general Customer class (the supertype), with Personal Customer and Corporate Customer as subtypes.

This phenomenon is also subject to different interpretations at the different levels of modeling. Conceptually, we can say that Corporate Customer is a subtype of Customer if all instances of Corporate Customer are also, by definition, instances of Customer. A Corporate Customer is then a special kind of Customer. The key idea is that everything we say about a Customerassociations, attributes, operationsis true also for a Corporate Customer.

Within a specification model generalization means that the interface of the subtype must include all elements from the interface of the supertype. The subtype's interface is said to conform to the super-type's interface.

Another way of thinking of this involves the principle of substitutability. I should be able to substitute a Corporate Customer within any code that requires a Customer, and everything should work fine. Essentially, this means that if I write code assuming I have a Customer, I can freely use any subtype of Customer. The Corporate Customer may respond to certain commands differently from another Customer (using polymorphism), but the caller should not need to worry about the difference.

Generalization at the implementation perspective is associated with inheritance in programming languages. The subclass inherits all the methods and fields of the superclass and may override inherited methods.

The key point here is the difference between generalization at the specification perspective (subtyping, or) and at the implementation perspective (subclassing, or implementation-inheritance). Subclassing is one way to implement subtyping. You can also implement subtyping through delegationindeed, many of the patterns described in Gamma, Helm, Johnson, and Vlissides (1995) are about ways of having two classes with similar interfaces without using subclassing. You might also look at Fowler (1997) for other ideas on implementations for subtyping.

With either of these forms of generalization, you should always ensure that the conceptual generalization also applies. I have found that if you don't do this, you run into trouble because the generalization is not stable when you make changes later on.

Sometimes, you come across cases in which a subtype has the same interface as its supertype, but the subtype implements operations in a different way. If you do, you may choose not to show the subtype on a specification-perspective diagram. I usually do if the users of the class would find it of interest that types may exist, but I don't if the subtypes are varied only because of internal implementation reasons.