< Day Day Up > |
When would we want to derive classes from CDRelease ? When the classes behave differently. We want to deal with classes with a single interface (the base class), and not be concerned with the implementation in the derived classes. A common indication that inheritance is desirable is the temptation to use switch statements in a class. For example, suppose Sam wanted to send an email to himself every time a CDRelease of type GoldieOldie or NewRelease was rented. With only a single class, the code might employ a switch statement (or an equivalent set of if statements), as in the rental_notification( ) function shown in Example 5-6. Example 5-6. Rental_notification method with switchvoid rental_notification( ) { switch(category_id) { case REGULAR_CD: break; case NEW_RELEASE_CD: case GOLDIE_OLDIE_CD: send_email( ); break; default: throw new ProgramException( ); } With the array shown in Example 5-3, the creation of a new CDCategory involves just adding another entry to the array. With behavior dependent on the category such as in Example 5-6, each appearance of a switch statement needs to be changed. If we need one switch statement in the entire implementation of a class, we should consider inheritance. If the same switch occurs in multiple places, applying inheritance is in order. Suppose that Sam also wants an email when a GoldieOldie or NewRelease is returned. With different objects, the rental_return_notification( ) method would have a switch that parallels the switch in rental_notification( ) . Using inheritance for CDRelease , as shown in Figure 5-3, eliminates these switch statements. Figure 5-3. CDRelease with inheritanceInheritance applied appropriately, such as in Figure 5-3, makes an excellent design. Using inheritance where it is not necessary, such as in Figure 5-1, can make designs harder to maintain.
As an alternative to inheritance, you can use delegation. The book Design Patterns strongly suggests that delegation is more powerful than inheritance. You can eliminate the inheritance of Figure 5-3 by delegating the behavior of CDRelease to classes implementing an interface ( CDReleaseCategory ) in a variation of the Strategy pattern (see Design Patterns ). Figure 5-4 shows the class outline for this approach. Figure 5-4. CDRelease with delegationThe constructor for CDRelease sets the cd_release_category attribute to an object of the corresponding type. The methods in CDRelease delegate their actions to that object, as illustrated in Example 5-7. Example 5-7. Example 5-7. CDRelease with delegationCDReleaseCategory [] cd_release_categories = { new RegularCD(), new NewRelease( ), new GoldenOldie( )}; CDRelease (CDCategory cd_category) { cd_release_category = cd_release_categories[cd_category]; } rental_notification( ) { cd_release_category.rental_notification( ); } rental_return_notification( ) { cd_release_category.rental_return_notification( ); } Suppose aspects of CDRelease 's behavior depended on a second attribute ”say, the genre of music (classical, rock, or folk). With inheritance, you would have to create a second inheritance hierarchy based on that behavior. With delegation, you only need to create a second attribute that refers to another class of objects that contains the desired behavior. |
< Day Day Up > |