Previous | Table of Contents | Next |
Based on this paradigm, the Menu class could be modified such that it encapsulates an additional instance variable named blocks that holds an array of blocks. Each block in the array would provide the message expression used to indicate that the corresponding label item had been selected. The implementation of the notify:with: method would change to the following:
notify: agent with: labelIndex Private - Send a message to the agent object that identifies the item that was selected ^(blocks at: labelIndex) value: agent
Although this design now accommodates an arbitrary number of labels in a menu and allows arbitrary messages for signaling selection, it requires a much more complex sequences of expressions to create a menu object. The original expression:
editMenu := Menu new labels: #(cut copy paste).
must be replaced with something like this:
selectionMessages := Array new: 3. selectionMessages at: 1 put: [:obj| obj cut]; at: 2 put: [:obj| obj copy]; at: 3 put: [:obj| obj paste]. editMenu := Menu new labels: #(cut copy paste) blocks: selectionMessages.
Smalltalk is able to avoid such complexity by providing a mechanism to directly parameterize the selector of a message send without using blocks. This mechanism depends on the capability to directly manipulate message selectors as objects. Smalltalk syntax provides a literal notation for such objects. A message selector immediately preceded by a # is a selector literal. Examples of such literals include the following:
#new #detect:ifNone: #>=
The value of a selector literal is a message selector object. Selector objects can be assigned to variables, passed as arguments, and compared for equality. Their most important property is their use as the argument to the perform family of messages. Via inheritance from class Object, all Smalltalk objects are able to respond to the following set of messages:
perform: perform: with: perform: with:with: perform: withArguments:
The first argument to the perform messages must be a message selector object. The effect of a perform message is to send a new message using the argument message selector to the original receiver of the perform message. The with arguments to a perform message provide the arguments for the new message. For example, the following pairs of expressions are equivalent:
aCustomer name aCustomer perform: #name 3 + 4 3 perform: #+ with: 4
By using perform:, we can modify our sample menu class to use an array of message selectors to specify the selection actions. The implementation of the notify:with: method would change to this:
notify: agent with: labelIndex Private - Send a message to the agent object that identifies the item that was selected ^agent perform: (selectors at: labelIndex)
Similarly, an instance-creation expression for the example would be as follows:
editMenu := Menu new labels: #(cut copy paste) selectors: #(#cut #copy #paste).
Exceptions are unusual or unexpected events that can occur during the execution of a Smalltalk program. When one of these exceptional events occurs, the program must take some special action. Some exceptional conditions are frequent enough that provisions for dealing with them are made an explicit part of a classs message protocol. For example, the message at:ifAbsent: is used to access an object from a collection. This message explicitly deals with the situation when the object is not found. A block is used to allow the programmer to specify the exceptional action:
result := aCollection at: key ifAbsent: [self defaultValue]
Other exceptional conditions are less localized or more difficult to anticipate. A more general mechanism is needed that can deal with exceptional occurrences that might occur during the entire scope of a computation. For these situations, standard Smalltalk provides a generalized exception-handling mechanism.
In the standard Smalltalk exception system, exceptions are represented as objects. Different kinds of exceptions are defined using different Smalltalk classes. Each exception class defines a default action that is performed upon occurrence of the exception if the programmer has not taken explicit action in anticipation of the exception.
The following are some commonly used exception classes; indentation indicates a subclass relationship:
Error | Any program error |
ZeroDivide | Attempt to divide by zero |
MessageNotUnderstood | Receiver of a message does not have method for it |
Notification | An event that usually can be ignored |
Warning | The user of the program should be informed |
A programmer can do something other than the default action associated with an exception by associating an exception handler with the execution of a block. Exception handlers are established by sending the message on:do: to a block:
[x / y] on: ZeroDivide do: [:ex| Transcript show: zero divide detected.]
This expression causes its receiver (the block containing x / y) to be evaluated as if the message value had been sent to it. If a ZeroDivide exception occurs while executing the block, the handler block (the argument to do:) is evaluated. In this example, the handler block causes a message to be displayed to the program user.
The handler block is passed a single argument (ex in this example), which is an instance of the actual class of exception that occurred. This object can be used to obtain additional information about the exception or to control what program action occurs when the exception block is completed. In many cases, the argument is not used and can be ignored.
Previous | Table of Contents | Next |