The Role Of Interfaces


Inheritance-Based Design

As you learned in chapter 11, inheritance plays a critical role in object-oriented design and implementation. However, as with all design strategies, it should be applied in right measure. In this section I want to raise your awareness of the appropriate uses of inheritance and the different forms an inheritance hierarchy can assume. Following these discussions the Person-Employee inheritance example originally presented in chapter 11 will be examined in the context of Meyer’s inheritance taxonomy and Coad’s inheritance criteria.

Three Good Reasons To Use Inheritance

There are at least three good reasons to use inheritance: 1) it provides you with an object-oriented design mechanism that enables you to think and reason about the structure and behavior of your code in terms of generalized and specialized classes, 2) it offers a measure of code reuse within your program, and 3) it provides you with a way to incrementally develop code.

As A Means To Reason About Code Behavior

Thoughtfully designed inheritance hierarchies help tame conceptual complexity. If you are fortunate enough to correctly formulate the abstractions (base classes/supertypes) at the upper-most level of the hierarchy then you can make reasonable assumptions about the behavior of the concrete implementations (derived classes/subtypes) when they are used in situations expecting supertype behavior. Well-designed inheritance hierarchies enable polymorphic behavior which is the cornerstone of object-oriented programming.

To Gain A Measure Of Code Reuse

Classes may contain code that can be potentially reused within your application. You need look no further than to the Java platform API for an example. The key to gaining code reuse via inheritance is to have correctly modeled the application domain in the class hierarchy in the first place and placed common behavior in classes that sit at the root of the inheritance hierarchy. (The root in this case means the top since inheritance hierarchies are typically modeled as inverted tree structures.)

To Facilitate Incremental Development

Inheritance facilitates incremental development by allowing programmers to extend existing classes (i.e. adopt existing behavior) when necessary and appropriate. Complex applications are typically built in an iterative fashion. Initially, an overall application architecture is laid down and one or more application features, each satisfying one, or perhaps several, outstanding requirements, are implemented with each iterative development cycle. (See chapter 3)

Forms Of Inheritance: Meyer’s Inheritance Taxonomy

In this book I have favored the use of four inheritance forms: subtype, extension, functional variation, and implementation. However, there are many forms of inheritance I have not discussed. These can be seen in Bertrand Meyer’s Inheritance Taxonomy shown in figure 22-1. Table 22-1 provides a brief description of each inheritance form. (Note: The lightly shaded rows of table 22-1 highlight the most often used inheritance forms.) Readers interested in a complete treatment of each inheritance form are referred to Meyer’s book which is listed in the references section at the end of this chapter.

image from book
Figure 22-1: Meyer’s Inheritance Taxonomy

Table 22-1: Inheritance Form Descriptions
Open table as spreadsheet

Inheritance Form

Inheritance Form

Inheritance Form Or Description

Model

Subtype

The most obvious form of inheritance. Used to model application domain objects into categories (base classes) and subcategories (derived classes). Base classes serve to specify behavior only and are therefore abstract. (Or, in Java, an interface.) Derived classes represent separate and distinct types.

 

Restriction

Derived classes introduce a constraint upon base class behavior. The constraint is usually applied to the base class invariant. (An invariant is a property that must hold true at all times. Invariants are discussed in detail in chapter 24.)

 

Extension

Derived classes introduce new behavior not found in the base class.

 

View

Derived classes do not fit nicely into disjoint types. Subclasses do not represent distinct types but rather various ways of classifying instances of the base class. View inheritance is best applied when base and derived classes are abstract (or interfaces).

Variation

Functional Variation

Derived classes redefine (override) base class methods.

 

Type Variation

Derived classes redefine base class method signatures. This type of inheritance is not authorized in Java. An overriding method in a derived class must have the exact method signature, including return type, of the base class method it’s overriding.

 

Uneffecting Inheritance

A derived class redefines a non-abstract base class method into an abstract method. This effectively removes the unwanted base class behavior.

Software

Reification

The base class represents a general kind of data structure, say a linked-list, and the derived class wants to adopt the functionality of the linked-list with the intent of making it into a different kind of data structure behavior-wise, say a queue. In the case of reification inheritance, the base class provides behavior (non-abstract).

 

Structure

Structure inheritance differs from reification inheritance in that the base class is abstract and provides only a set of specifications for the behavior of the data structure. ( abstract methods ). The derived class may provide full or partial implementation of the behavior specified by the base class. This form of inheritance occurs frequently in the Java Collections API.

 

Implementation

The derived class inherits the behavior specified by the base class and uses it asis.

 

Facility

Constant

The base class consists of static final fields (constants) and methods whose bodies are executed only once to return a reference to a common object. A method that returned a singleton instance would fit the bill.

  

Machine

The base class consists of methods the derived class finds useful to perform its mission.

Coad’s Inheritance Criteria

Peter Coad, in his book Java Design: Building Better Apps And Applets , provides a set of five checkpoints that can be used to ensure the effective use of inheritance. The inheritance form(s) each checkpoint seeks to avoid is listed in parentheses.

  1. The derived class models an “is a special kind of,” relationship to the base class not an “is a role played by a” relationship. (view)

  2. The derived class never needs to transmute to be an object in some other class. (view)

  3. The derived class extends rather than overrides or nullifies the base class. (functional, uneffecting)

  4. The baseclass is not merely a utility class representing functionality you would simply like to reuse. (constant, machine)

  5. The inheritance hierarchy you are trying to build represents special kinds of roles, transactions, or devices within the application domain.

Person - Employee Example Revisited

Given Meyer’s taxonomy of inheritance forms and Coad’s criteria for the effective use of inheritance, let’s reevaluate the Person-Employee inheritance hierarchy originally presented in chapter 11. It would be helpful if you print out the source code for this example to refer to while reading the assessment presented in this section. Figure 22-2 gives the UML class diagram.

image from book
Figure 22-2: Person-Employee Inheritance Diagram

Referring to figure 22-2 — the Person class provides complete functionality for a generic person. The Person class is fully implemented and therefore not abstract. The Employee class utilizes implementation inheritance by extending the Person class, and subtype inheritance by implementing the Payable interface. However, since the Employee class fails to provide an implementation for the Payable interface’s pay() method it is declared to be abstract and pushes the responsibility of pay()’s ultimate implementation to its derived classes. The HourlyEmployee and SalariedEmployee classes both employ implementation, subtype, and functional variation inheritance since each fully accepts Employee’s Person-based behavior, each is a subtype of Employee, which is a subtype of both Payable and Person, and each overrides the pay() method to provide custom-derived class functionality.

When evaluated against Coad’s criteria this design fails many of the checkpoints: 1) The Employee class does not strictly model an “is a special kind of” relationship between itself and the Person base class, 2) The Person class, which sits at the root of the inheritance hierarchy, does not model a role, transaction, or device, and 3) although not evident in this limited example, subclasses may need to transmute to other subclass types. This difficulty might not be encountered until we are asked to extend the current design in response to a seemingly innocent feature request and found we had programmed ourselves into a tight corner indeed.

The following questions arise from this example: “Can this design be improved and how?” and “Is the current design completely invalid and unusable?” I will address these questions after discussing the role of interfaces, polymorphism, and compositional design in the following sections.

Quick Review

There are at least three good reasons to use inheritance: 1) it provides you with an object-oriented design mechanism that enables you to think and reason about the structure and behavior of your code in terms of generalized and specialized classes, 2) it offers a measure of code reuse within your program, and 3) it provides you with a way to incrementally develop code.

The most often used forms of inheritance include subtype, extension, functional variation, and implementation. Coad’s criteria provides five checkpoints that can be used to validate the use of inheritance.




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

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