Previous | Table of Contents | Next |
This line is more concise and has the advantage that a programmer cannot inadvertently forget to initialize the new instance. Such a message can be implemented by using a class method. Class methods are similar to instance methods, but instead of extending the behavior of a classs instances, they extend the behavior of the class object itself. Class methods are defined in a separate section of the class definition template:
Class Name | Customer |
Super Class | Object |
Instance Variables | name address customerNumber |
Instance Methods | |
name: initName address: initAddr: customerNumber: initNumber initialize the state of the receiver to the argument values name := initName. Address := initAddr. customerNumber := initNumber | |
name return the name of the customer ^name | |
address return the current address from the receiver ^address | |
Class Methods | |
name: initName address: initAddr customerNumber: initNumber create and initialize a new instance using the argument values | c | c := self new. ^c name: initName address: initAddr customerNumber: initNumber |
The new method just defined essentially encapsulates the compound message sequence used to create and initialize a new instance. However, in this example we choose to use a sequence of expression and a temporary variable instead of a compound message expression. This line:
| c |
is a declaration of a method temporary variable named c. A method can have an arbitrary number of temporary variables that are declared by listing them between vertical bars immediately before the first statement of the method. The scope of a method temporary variable is limited to the method in which it is declared. Each time a method is activated, a new set of temporary variables is created and initialized to nil for use by that activation of the method.
This method first creates an uninitialized customer instance by sending the message new to self. Within a class method, self is bound to the class object. In this case, the value of self is the class object, Customer, so the new message creates a new instance of that class. This object is assigned to the temporary variable c. Finally, the message name:address:customerNumber: is sent to the value of c. This message invokes the instance method whose selector is name:address:customerNumber: and which initializes its receiver, the new instance.
Note that an instance method is still needed to initialize the instance variables of the new instance. This is because the instance variable names are accessible only to instance methods. A class method cannot directly access the instance variables of instances of the class. Like any other object, a class object must use messages to access the state of other objects, including its instances.
This example also illustrates a use of polymorphism. Different methods with the same selector (name:address:customerNumber:) are defined for both the instance behavior and the class behavior. A message expression using that selector invokes one or the other of the methods, depending on what object was the actual receiver of the message. Looking at an expression such as the following out of context, it is impossible to determine which method would be executed:
c name:Joe address:Any City customerNumber: 1
If the actual receiver (the value of c) is the class object, the class method is invoked. If the actual receiver is an instance of Customer, then the instance method is invoked.
Just as it is useful to define state variables for instance objects, it is also useful to be able to define state variables for class objects. Consider, for example, the situation in which we needed to count the number of Customer objects that were created. One way to do this would be to extend the class method name:address:customerNumber: to maintain a counter:
name: initName address: initAddr customerNumber: initNumber create and initialize a new instance using the argument values | c | c := self new. customerCount := customerCount + 1. ^c name:initName address:initAddr customerNumber: initNumber
The remaining issue is how to declare the variable customerCount. Declaring it as a method temporary would not work because new temporary variables are created and initialized to nil each time a method is executed. Instead, customerCount can be made a class instance variable. Class instance variables are declared in a separate section of the class definition template. A class instance variable is an instance variable of a class object. As such, a class instance variable is accessible from the class methods, but because it is part of the state of the class object, it retains its value across multiple method invocations.
Just as instance variables cannot be accessed by class methods, class instance variables cannot be accessed by instance methods. Occasionally, it is useful to have a variable that is shared by all instances of a class, and possibly with the class object itself. For example, we might need to enhance our customer object to include an instance variable that records the customers personal credit limit. However, because we have already written code using the original definition of Customer, we would prefer to make this change in a way that did not require us to change every place we have already coded name:address:customerNumber: to create or initialize a new instance. One way to accomplish this would be to redefine the instance method for this selector so that it initializes the credit limit instance variable to a default value. Doing this, the instance method might be rewritten as follows:
name: initName address: initAddr customerNumber: initNumber initialize the state of the receiver to the argument values name := initName. address := initAddr. customerNumber := initNumber. creditLimit := 500. set credit limit to default value
Previous | Table of Contents | Next |