A Worked Example of ADT, Class, Object derivation from real world:
e.g. Remote Printer Spooler
First we must identify what we print :- usually a file, which is a
vector of bytes. This may be refined by noting that different printers
accept different kinds of file, such as text only, or postscript.
This means we refine both the type of file, and a new type for printer
which was opaque previously.
Then we identify some useful operations (using the American
convention: # is ;SPM_quot;number of;SPM_quot;):
-
Add ;SPM_quot;file;SPM_quot; to printer queue - succeeds returns q# or fails
-
Remove q# from queue - succeeds, returns ok, or q# not in queue
-
List queue
This has introduced the idea that the printer is spooled, and that
there is an abstract data type ;SPM_quot;queue;SPM_quot;.
The ADT would say that q is ordered set of files (size/byte array and
type) + q#s
-
adding to q appends the set
-
removing checks q# is in queue set, and removes
-
initial queue is empty, q can have a maximum size, so add can fail.
The Class ;SPM_quot;printer;SPM_quot; now has an internal private type, queue, and some
public operations, add, remove and list.
This is implemented as an Object - the set of code that implements the
type and operations, and an interface.
A number of these may be instantiated (bought to life)
as server processes in a system
with different parameters (such as printer type). We can see how this
can easily be refined to add printer usage accounting per user and so
forth.
This example will be expanded in chapter 2, then used in chapter 3
to illustrate concurrent access and how list (= read) can be concurrent,
while add/remove (= writer) cannot.
Storage for objects (the data structures and code
that implements them) should be automatically allocated and
de-allocated as appropriate.
Modules/Objects are just higher level types than the base types of the
language that implements them - this is the <#192#> Class/Superclass<#192#>
relationship.
A Class describes something which either extends an existing class,
or else limits the functionality of an existing class - this is called
<#193#> refinement<#193#>.
Operations (methods) used within a program can refer to objects of
more than one type - the meaning of the operation is determined by
each implementation in the class. (e.g. append can add a file to a
printer queue, or add a block to a file). This is called
<#194#> polymorphism<#194#>
and implies that <#195#> dynamic binding<#195#> of operations to instances of objects
can happen. We shall see that this is very appropriate in distributed
systems.
Finally, it should be feasible to define new classes that can refine
more than a single previous class. This is called <#196#> multiple
inheritance<#196#>. For instance, when implementing a calendar, a programmer
should be able to draw on existing modules/classes for a simple
algebra of time, and on an existing implementation of a simple windowing
system and on an existing spreadsheet object...).
The notion of transparency described at the beginning of this
chapter is one justification for the Object Oriented Approach.
The mechanisms that implement the transparencies are base methods in
the base classes in the system. Once these have been identified, the
programmer can choose whether or not to use them and can combine them
through multiple inheritance to form any class of system appropriate.
Further justifications include:
-
The Object Oriented methodology is to increase software
re-usability.
-
A Collection of methods determines the object Class (or type).
In a distributed system, this can help identify uniquely a given
object. Clearly, it is helpful to distinguish service types, even just
so that clients don't start talking to a tape drive when what they really
wanted was a printer. However, the strong typing implied by this
approach can be taken further towards helping the programmer with
conformance. We look at formal approaches in Chaoter Five.
-
This in turn aids clear design and implementation of a system.
The implementation of the object is accessed by a collection of
methods only, and there are several mechanisms used to
implement <#199#> invokation<#199#> of a method.
Typically <#200#> message passing<#200#> is used in centralized Object
Oriented Systems. This is also the case in distributed systems.
Figure 1.6 Message Passing
One process sends a message to another. Once the primitive send
operation has completed, the sender is unaware of the fate of the
message. At some later stage, the receiver may issue a receive
operation, or not. It may fail. For example, in the Smalltalk
programming environment, sending the message ;SPM_quot;+1;SPM_quot; to ;SPM_quot;2;SPM_quot; results in
;SPM_quot;3;SPM_quot;. Note, though that there is no implied ;SPM_quot;returned;SPM_quot; result, and even
if there were, it might be that there is no strict interleaving of
method messages and result messages.
In practice, the level of granularity of Objects in
a Distributed System will be larger than that of centralized object
oriented systems.
In Distributed Systems, Remote Procedure Call (also called
Remote Operations) has been used as the mechanism.
Figure 1.7 Remote Procedure Call
A process in one address space executes a procedure in another address
space. Apart from access to ;SPM_quot;global;SPM_quot; variables, the procedure call is
synchronous, exactly as a normal (local) procedure call is.
The fine detail of an implementation <#201#> hides<#201#> the various
mechanisms that implement some of the Transparencies like:
-
Concurrent Access control for Consistency.
-
Replication for fault tolerance.
-
Migration for performance and heterogeneity of machines.
-
Scaling
This is discussed further in chapters 2 and 3.
The task for the programmer given a specification
is to define an Object that meets this specification. There are then
several steps in the process of building a service.
-
In the process of designing the object, the programmer may call on
existing methods/types.
-
It may be that this object is so similar to previous types of
objects that it may be a sub class, and inherit all their methods.
-
The service that the object will provide is defined in an Interface.
This will be the set of public types and procedures/methods available
on the object.
-
This interface is compiled, and linked with the code that implements
the private methods the service requires.
-
This may then be linked with a variety of existing system objects
to provide different performance/reliability functionality.
-
This is then executed to provide the service. This service is an
interface to an instance of the class the object was drawn from.
Example
Here we present an example of the use of the object oriented approach applied
to a parts data base for some fictitious Automotive Company:
Enterprise View:
Figure 1.8 Views
verbatim2
A collection of different access operations might be designed for such
a database, depending on the user. The distributer, manufacturer may
need highly reliable (but perhaps not completely consistent) access.
The accounts department might require complete consistency in any
data but not have any performance constraints.
So associated with the views of the data are methods for accessing
them (this is different from the conventional relational database,
where the methods are part of the database implementation, not part of
the data or relations).
An alternative view might be from marketer's system:
Figure 1.9 Alternate Views
verbatim3
The relevant data may be distributed in a number of different systems,
in databases acquired from different suppliers. The users may need to
access the data with different toolsets. For instance, the Automotive
designer may wish to access the chassis/body information from within a
CAD/CAM system. The Legal department may wish to access the exhaust
system information from a legal database access system.
A complete specification would address details as small as printing of
correct forms for invoiceing for a part, or for printing legal
certificates of roadworthiness, an so forth.
-
Viewpoints:
Enterprise, Information, Computational, Engineering and Technology;
-
Transparencies:
Access Location Concurrency Fault Migration Performance Scaling
How can we decompose the design of the access system to unify the
optimal amount of the subsystem so that software effort is not too
large? The answer lies in the way that the Object Oriented approach
allows us to abstract modules from the requirements and identify
common subsystems/modules/objects by the processes of refinement and
inheritance.