This paper presents the results of research toward a simple graphical notation for defining object methods.
There are several kinds of graphical notations that have historically been used to model the behavior of software systems. These notations are useful analytic tools that can help us understand what operations a system needs to support, as well as how a system will implement those operations in broad terms. While each notation uses some combination of graphical nodes and connectors, the semantics of the nodes and connectors is different for each notation. Thus, each notation provides a different "architectural view" of how a system behaves. The following table shows a schema of these relationships.
|Object Interaction||Instances||Method Invocation||Control|
|State Transition||States||Events, Transitions||State|
|Data Flow||Processes, Sources, Sinks, Stores||Data Flows||Data|
Unfortunately, these notations typically require human interpretation and translation into a specific programming language during system construction. So, while these notations are useful and should be retained for documenting and viewing overall systemic behaviors, their limitations make them less suitable for detailed definition and implementation of object methods.
Off and on over the past few years, I've considered the possibility of modeling object methods visually. I experimented with several alternative graphical notations, all of which left me dissatisfied. I eventually became skeptical regarding whether any visual notation would satisfy my desires. I wanted a notation that would provide:
Two key insights finally led me to the method modeling notation described in this paper.
The last insight was the key one that led me back to this pursuit with some enthusiasm. The notations I chose for modeling methods were inspired in large part by the notations used in OMT and UML to model the relationships between objects. Object methods can be modeled using notations consistent with those used for object models.
Method models apply these same ideas to the behavior of object methods - messages sent in object methods are modeled as relations between objects. Simply put, the objects are the operands and the associations between them identify the operations. We see something like this in object models - object names are typically nouns or noun phrases and association names are typically verbs or verb phrases. Thus, associations between objects can be used to model both structural relations (as in object models) and behavioral actions (as in method models).
Because method models can define the detailed behavior of object methods, they can be used to generate source code in a target programming language, or they can be compiled directly to executable code. Throughout this paper, the examples will use Smalltalk because its pure object-orientation and simple syntax provide the best basis for method modeling.
Methods in object-oriented programming languages are composed of two kinds of structural elements: objects and scopes. The following notations identify the structural elements used to define an object method.
In method models, an element with a bold outline indicates the definition of the element. The location of an element definition identifies the scope of the definition. Thus,
Each object-oriented programming language has a set of elementary data types for which constants may be defined, as well as some mechanism(s) for self reference. In method models, an element with bold contents identifies a constant value or self reference. The following notations identify the kinds of constants and self references supported by Smalltalk.
Each method is defined relative to a class. Message connectors identify the name and argument(s) of a method. The notations below show templates of method definitions. The first template shows the definition of a unary method - i.e., the message receiver is the only method argument. The second template shows the definition of a binary method. Methods with more arguments simply add more connectors (and their associated keywords in Smalltalk), one for each additional method argument - see the elementary messages for examples.
In method models, the notation for blocks resembles that used for methods because methods and blocks both identify scopes. As with methods, block arguments are connected to the outside of the block to show that their values originate from outside the block. Blocks can be nested inside methods or other blocks.
In method models, the notations used for modeling message sends resemble object model associations. In message models, objects are associated with each other by a message connector. The message receiver is identified by the arrow of the message connector. The following notations identify the kinds of message connectors used to model Smalltalk messages.
Of course, to model message sends in other programming languages, we need only eliminate the keywords associated with the arguments that follow the first one.
Imperative programming languages provide mechanisms for altering the flow of control. Because Smalltalk is a pure object language, we can model these mechanisms easiest using control idioms from Smalltalk. Smalltalk implements its control mechanisms using blocks. For example, the following diagrams show how to model simple Smalltalk alternatives.
When building object systems, switch (case) statements are often eliminated using polymorphic message designs. However, many OO languages support this kind of selection mechanism. So, it is appropriate to provide a way to model them, as we will certainly encounter them in real systems.
To remain consistent with the previous method model notations, we need a way to model switch statements using object messages. Let us begin by considering how we might model switch statements in Smalltalk. Once the following methods have been added to the base Object class, Smalltalk can express switch statements that look very much like those found in other programming languages.
!Object methods! case: caseValue do: aBlock self = caseValue ifTrue: aBlock! defaultDo: aBlock aBlock value! !
Given the foregoing, Smalltalk can express switch statements as a series of cascaded case:do: messages sent to a switch value. This consideration clarifies how to model switch statements using pure object messages.
switchValue case: 1 do: [ "...thisBlock..." ]; case: 2 do: [ "...thatBlock..." ]; "...etc..." defaultDo: [ "...defaultBlock..." ]
Notice however that returning self is the only way to break out of each case block and terminate the message cascade, as well as the enclosing method. This imposes some limitations on how message-based switch statements can be used. They can only be used to terminate a method. While this does not model the behavior of switch statements completely, it does approximate them fairly closely, preserving their overall intent and syntactical form.
For loop constructs, Smalltalk again provides an excellent source of control idioms. For example, the following diagrams show how to model simple loops and infinite loops.
And, the following diagrams show how to model loops that are indexed.
This example shows how to model a very simple Smalltalk method graphically.
!Example class methods! sortBlock ^[ :a :b | a <= b ]!
Note that this method returns a block. This example shows how a method return is modeled as a binary operation - the method scope returns the result. This example also shows an important aspect of Smalltalk: blocks are also objects. The following Smalltalk expression shows an example of how such a sortBlock is invoked.
Example sortBlock value: 1 value: 5
This example shows how to model a more complex Smalltalk method. It shows how assignments are modeled as binary operations. It also shows the use of a block nested inside a method scope.
!Example methods! circumference: radius | circumference | circumference := 0. radius > 0 ifTrue: [ | diameter | diameter := radius * 2. circumference := diameter * Float pi. ]. ^circumference!