5.3. Appropriate Inheritance

 <  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 switch
 void 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 inheritance

Inheritance 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.

AVOID PREMATURE INHERITANCE

Inheritance needs time to evolve .


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 delegation

The 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 delegation
 CDReleaseCategory [] 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  >  


Prefactoring
Prefactoring: Extreme Abstraction, Extreme Separation, Extreme Readability
ISBN: 0596008740
EAN: 2147483647
Year: 2005
Pages: 175
Authors: Ken Pugh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net