Previous | Table of Contents | Next |
Much of the power of object-oriented programming comes from the use of inheritance to build families of related classes. Subclasses can build on an abstraction defined by a superclass and can change it in many different ways. Some common uses of subclassing include:
By carefully structuring a class hierarchy, inheritance can be used to share and reuse code among a number of classes. In a well-designed class hierarchy, each class should add or replace the minimal amount of code needed to distinguish it from its superclass. However, a subclass can replace inherited code only in method-sized units. This means that the structure of a superclasss methods has a great impact on how easy it is to subclass it.
One goal in designing a Smalltalk subclass is to minimize the amount of code that must be physically copied from the superclass. Copied code is the antithesis of code reuse and makes programs significantly more difficult to maintain. Large, complex methods in a superclass are difficult to override without significant textual code copying. Consider a 60-line method and a subclass that needs to change 1 line in the middle of that method. Because of the method-level granularity of subclass code replacement, the programmer of the subclass would have to duplicate 59 lines of code in order to replace that single line. To avoid this problems, most Smalltalk programmers use procedural decomposition to split complex methods into a number of short, simple submethods. Any individual submethod can then, if necessary, be overridden by a subclass without the need for duplicating or overriding other submethods.
4.3.4.1. The self Identifier
Procedural decomposition in Smalltalk requires the use of the reserved identifier self. Within a method, the value of self is the actual object that was the receiver of the message that activated the method. self can be used in any context in which a literal is allowed, including as an argument or a receiver of a message. Procedural decomposition is accomplished within a method by using self as the receiver of messages. Consider these lines, for example:
someMethodThatPerformsAComplexComputation The algorithm used by this method requires pre- and post- conditioning of its data. self preConditionData. self complexAlgorithm. self postConditionData
The class also defines methods named preConditionData, complexAlgorithm, and postConditionData that implemented the details of the three parts of the computation. A subclass could override any of the three methods without having to override or duplicate the other two methods.
4.3.4.2. The super Identifier
Sometimes in creating a subclass, the need arises to extend rather than replace an inherited method. For example, some additional code might need to be added at the beginning or end of an inherited method. This can be accomplished using the reserved identifier super. This identifier can be used only as the receiver of a message. super is like self in that it is a reference to the object that was the receiver for the message that activated the current method. However, a message send with super as its receiver invokes the method for the messages selector from the behavior of the superclass of the class that defines the method containing the message expression. If the selector of the send to super is the same as the selector of the current method, the send will invoke the method that is overridden by the current method.
A subclass can extend a method with additional code by overriding the superclasss method and including within the body of the new method a message send to super using the methods selector. Consider the following, for example:
someExtensibleThing:arg Override the inherited method to do additional computations before and after the inherited method self beforeAction. super someExtensibleThing:arg. self afterAction.
In section 4.3, we mentioned that the value associated with a class name is a class object and that new instances are typically created by sending messages to a class object. Where are the methods that implement these messages defined? The following sections address class methods, class instance variables, and class variables.
The implementation for standard methods such as new and new: are inherited from the definition of Object and are available to all classes that are direct or indirect subclasses of Object. However, it is occasionally useful to define additional methods for class objects. For example, instead of using a sequence of messages to create and initialize an object such as this one:
Customer new name:Joe address:Any City customerNumber: 1
we might prefer to use a single message that both creates and initializes a new instances:
Customer name:Joe address:Any City customerNumber: 1
Previous | Table of Contents | Next |