A role is an idealized description of an object in the context of a pattern of collaborating objects. Through our policy of divide and
conquer, we focus on the object aspects that are relevant for the role
model's area of concern.
An object specification is a partial description of an implemented object
The object specification describes an object which will actually be implemented. The role is made more concrete when we promote it to an object specification. The programming language concept
corresponding to an object specification is a class.
The default is that there is a one-to-one relationship between object specification and class, but in general there is a many-to-many relationship between them.
The simplest situation is if we create a complete object specification model for the set of classes we want to implement. In figure 2.17 on page 78??, we defined a Client-Server model and a Data transfer model. In figure 3.18 on page 121??, we used these models as base models and derived a File send/receive model. In figure 4.8, this model has been promoted to an object specification model ready for implementation in two classes, one for each role.
src int Send/receive Client Send/receive Server Figure 4.8 File send/receive object specification model
29 March 1995 23:05 4.2 The relationship between a role model and its implementation
©Taskon 1992. Page 165 Bridge to implementation
Many classes may implement the same role
We may choose to program different classes that implement the same object specification. The classes may have different space/speed tradeoffs, or different functionality, even if their instances play the same role in the current role model. An example is that we could implement a dummy class for the Client role which can later be replaced by a selection of different product implementations for simple and sophisticated end user file manipulation systems.
The different subclasses of Point for Cartesian and polar coordinates given in the Modeling a Point example in section 4.1.2 also illustrate several implementations of the same object specification.
We want to stress that the OOram method does not insist that we create a complete set of derived models and object specifications. On the contrary, the OOram method specifies that we only create the models needed for our understanding of the system, and that models and source code together constitute the system documentation. Most well-defined models should be sufficiently independent to render a formal synthesis operation superfluous. The implementor implements the classes directly from the several base models so that the class instances will play all the required roles.
One example is the activity network model of section 1.2.2 on page 19??. The role model is quite sufficient to specify the single class that plays the roles of Predecessor, Job, and Successor.
A class may implement many roles
Another example is the two base models for the FTP file transfer. These models can be promoted to object specifications as shown in figure 4.9, leaving it to the implementor to make his two classes play the roles of (Client, Source, Destination) and (Server, Source,
Destination) respectively. dst src rsp int Client Server Source Destination Figure 4.9 Two object specification models specifying the FTP file transfer service
29 March 1995 23:05 4.2 The relationship between a role model and its implementation
Bridge to implementation ©Taskon 1992. Page 166
Our naming convention is that role names are alphabetic, and classes are given the names of the primary role followed by a numeric suffix. The class for the Client role could be named Client1, and the class for the Server role could be Server1. We thus follow the naming
conventions for classes in our programming language when we name our roles.
We name classes with numeric suffix
The object specifications of figure 4.8 and 4.9 both lead to the following class definitions:
// Class: SendReceiveClient1 class SendReceiveServer1; class SendReceiveClient1 { public: SendReceiveClient1(); ~SendReceiveClient1(); private:
SendReceiveClient1(const SendReceiveClient1&); // Avoid copy
SendReceiveClient1& operator = (const SendReceiveClient1&); // Avoid assignment SendReceiveServer1* server; }; // end of SendReceiveClient1 // Class: SendReceiveServer1 class SendReceiveServer1 { public: SendReceiveServer1(); ~SendReceiveServer1(); private:
SendReceiveServer1(const SendReceiveServer1&); // Avoid copy
SendReceiveServer1& operator=(const SendReceiveServer1&); // Avoid assignment SendReceiveClient1* client;
}; // end of SendReceiveServer1
Similarly in Smalltalk:
Object subclass: #SendReceiveClient1 instanceVariableNames: 'server '. Object subclass: #SendReceiveServer1
instanceVariableNames: 'client '.
A class may be derived from a base class, and a group of classes may be derived from a group of base classes.
An object specification model can be derived from a base object specification model. This is an open invitation to let the derived classes be derived from the corresponding base classes. We could, for example, define base classes for the Client and Server roles and augment them with send/receive functionality in the subclasses. In Smalltalk:
Synthesis can be mapped on to class inheritance
Object subclass: #Client2
instanceVariableNames: 'server '. Object subclass: #Server2
instanceVariableNames: 'client '. Client2 subclass: #SendReceiveClient2
instanceVariableNames: ''.
Server2 subclass: #SendReceiveServer2 instanceVariableNames: ''.
29 March 1995 23:05 4.2 The relationship between a role model and its implementation
If our programming language includes facilities for multiple
inheritance, we could implement classes for the Client, the Server, the Source and the Destination roles. We could then implement the different combinations described in figures 3.16 through 3.18 by suitable derivations. An example for the file send/receive case is shown in figure 4.10. Figure 4.10 Possible multiple inheritance hierarchy Destination2 SendReceiveServer2 SendReceiveClient2 Source2 Server2 Client2
The following C++ code uses multiple inheritance to implement the class structure of figure 4.10. You may find that it is more interesting than it is practicable: // Class: Client2 class Client2 { public: Client2(); ~Client2(); private:
Client2(const Client2&); // Avoid copy
Client2& operator=(const Client2&); // Avoid assignment }; // end of Client2 // Class: Source2 class Source2 { public: Source2(); ~Source2(); private:
Source2(const Source2&); // Avoid copy
Source2& operator=(const Source2&); // Avoid assignment }; // end of Source2 // Class: Destination class Destination { public: Destination(); ~Destination(); private:
Destination(const Destination&); // Avoid copy
Destination& operator=(const Destination&); // Avoid assignment }; // end of Destination // Class: Server class Server { public: Server(); ~Server(); private:
Server(const Server&); // Avoid copy
Server& operator=(const Server&); // Avoid assignment }; // end of Server
// Class: SendReceiveClient2 class SendReceiveServer2;
class SendReceiveClient2 : public Client2, public Source2, public Destination {
4.2 The relationship between a role model and its implementation 29 March 1995 23:05
Bridge to implementation ©Taskon 1992. Page 168
public: SendReceiveClient2(); ~SendReceiveClient2(); private: SendReceiveServer2* client; }; // end of SendReceiveClient2 // Class: SendReceiveServer2
class SendReceiveServer2 : public Server, public Destination, public Source2 { public: SendReceiveServer2(); ~SendReceiveServer2(); private: SendReceiveClient2* client; }; // end of SendReceiveServer2
When we use automatic program generators, we find it convenient to distinguish between the automatically generated code and the manual extensions. We let the tools create (abstract) classes with a zero suffix, e.g., Client0, and the programmer extends and modifies this code in a subclass, e.g., Client1. The advantage is that we can regenerate the superclass when the models are modified without interfering with the manually prepared code. The disadvantage is the added complexity caused by doubling the number of layers in the class structure, see figure 4.11.
Automatic code generators useful, but less flexible than human programmers
Client0 automatically generated
abstract (base) class
Client1 manually coded
concrete class Inheritance
This class may be updated automatically at any time ...
... without destroying the manually created code in this class. Figure 4.11 Automatically generated superclasses, manually prepared subclasses
We have been using automatic code generators for Smalltalk and Eiffel, and find them very useful for creating executable
specifications and prototypes. We usually remove the abstract layer from the final production code to avoid the unnecessarily deep class hierarchy, and use checking programs to maintain the correspondence between object specification and code in the maintenance phase.
29 March 1995 23:05 4.2 The relationship between a role model and its implementation