- Uses of Object Inheritance
- The Most Useful Aspects of Object Inheritance
- Implementing Object Inheritance
- Conclusion
Implementing Object Inheritance
Now that we know what we want and what we wish to avoid, the question remains: How do we implement object inheritance in a language that provides no direct facility for it? A good language to use for an example implementation is Java. Java has no explicit facility for object inheritance. It is class-based, and it has many of the same features and limitations as several other object-oriented languages.
Creating a Profile
One feature (or limitation, depending upon your point of view) that is common in many languages is compile-time type checking. This feature allows the compiler to catch many common programming mistakes. In the case of object inheritance, however, it makes the second of our desirable aspects a little more difficult to implement. The child cannot stand in for its parent unless they can be viewed as the same type.
Regular class inheritance would accomplish this, but is not what we really want. We do not want to inherit the implementation. In fact, inheriting the implementation would make it likely that some method we intended to override would be forgottenleading to unexpected (and somewhat difficult to find) errors. Also, class inheritance is not selective; it is all or nothing. We need to be able to choose which properties and services will be shared between the parent and its children.
Fortunately, Java also has a facility for a purely abstract inheritance of interfaces. Java interfaces define only the method signatures, and leave implementation up to concrete classes. If we specify that the parent and its children implement the same interface, then other objects can refer to both parent and child through this interface without worrying about which specific type they are messaging.
We call the inheritable interface the "profile" of the parent. The profile interface includes all the services and data accessing methods that can be inherited. From the actorrole example, a person's profile would include things such as read-only accessors for the name, address, and any other globally useful contact and demographic information. It would not include services that change information or involve the actor's roles. The Java code for the PersonProfile interface appears as Listing 1.
Listing 1. The PersonProfile Interface
public interface PersonProfile { String getName(); Address getAddress(); PhoneNumber getPhoneNumber(); }
Making the Parent Responsible
Our other necessity is to have the child answer messages in the same way and with the same values as its parent. This can be accomplished in two basic ways: copying and forwarding.
Copying, in this situation, would mean that the values of the parent's properties (and inheritable collaborations) would be copied into the child. This causes a normalization problem. The data is now kept in more than one place. Changing the values in the parent will mean that the child is out of sync.
Forwarding means that the child's implementation of the profile interface will directly pass the request on to the parent. This implementation makes the parent solely responsible for access to its information. This is not only better normalization it is better encapsulation.
Listing 2. Implementation of Message Forwarding
public class Employee implements PersonProfile { protected Person parent; // The rest of the Employee code // goes here. public String getName() { return this.parent.getName(); } public Address getAddress() { return this.parent.getAddress(); } public PhoneNumber getPhoneNumber() { return this.parent.getPhoneNumber(); } }
There is one problem with message forwarding. As you can see in Listing 2, it makes for very tedious code writing. Each method implementation is basically the same: "Hey parent, do this for me, okay?" As with many of the more tedious facets of programming, this cannot be helped until either more languages have a facility for it built in, or tools are created to automatically generate the code.
Overriding Object Inheritance
So far, we have talked only about the need for the child to duplicate the responses of its parent. What about cases in which the child does need to change its appearance?
For example, in a retail environment, a general descriptive item object and a more detailed specific item object combine to represent a shirt. The item would provide information such as brand, style, possibly a textual description, and a price. The specific item would provide details such as the SKU number, the size, and color. Suppose, however, that certain SKUs are not selling wellno one wants the lime green, XXX-small shirts this year. The merchant would like to change the price for just those particular shirts.
One solution is to simply not object-inherit the price. The price property would move from the item to the specific item. The problem with this is that now any time the merchant wants to change the price on all the shirts (end-of-season clearance), each SKU must be changed separately.
A better solution, demonstrated in Listing 3, is to provide the child object with its own price attribute. Although this price is empty (null), the child forwards the message on to its parent. If this value is set, however, the child's value overrides the value of the parent. This same scheme works for collaborations as well.
Listing 3. Overriding a Property Value
public class SpecificItem implements ItemProfile { protected Item parent; protected Currency price; // The rest of the SpecificItem code // goes here. public Currency getPrice() { if (this.price == null) { return this.parent.getPrice(); } else { return this.price; } } }