In addition, one must establish the relationships between the object classes constituting the system and delineate the facilities the system offers to the user. Such facilities are usually derived from a requirements document and may be formally specified in terms of abstract operations on the system.
In this section we will look at
the means we have available to express
the properties of our object model,
and we will study how we may
employ abstract specifications of system
operations to arrive at the integration
of user actions and the object model
underlying a system in a seamless way.
The approach sketched may be characterized
as event-centered.
Structural versus behavioral encapsulation
Object-oriented modeling has clearly been inspired
by or, to be more careful, shows significant
similarity to the method of semantic modeling
that has become popular for developing information systems.
In an amusing paper,
In contrast, objects (in the more general sense) usually hide object and non-object data members, and instead provide a method interface. Moreover, object-oriented modeling focuses on behavioral properties, whereas semantic modeling has been more concerned with (non-behavioral) data types and (in the presence of inheritance) data subtypes.
Relations, as may be expressed in the entity-relationship
model, can partly be expressed directly in terms
of the mechanisms supported by object-oriented
languages.
For instance, the is-a relation corresponds
closely (although not completely) with
the inheritance relation.
See slide 3-challenges.
Both the has-a and uses relation is
usually implemented by including (a pointer to)
an object as a data member.
Another important relation is the is-like relation,
which may exist between objects that are neither related
by the inheritance relation nor by the subtype relation,
but yet have a similar interface and hence may be regarded
as being of analogous types.
The is-like relation may be enforced by parametrized
types that require the presence of particular methods,
such as a compare operator in the case of a generic
list supporting a sort method.
Model-based specification
In the following, an outline of
the specification language Z will be given.
More importantly, the specification
of a simple library system will be discussed,
illustrating how we may specify
user actions in an abstract way.
(The use of the Z specification language
is in this respect only of subsidiary
importance.)
In the subsequent section, we will look
at the realization of the library
employing an abstract system of objects
and events corresponding to the user
actions, which reflects the characterization
given in the formal specification.
The specification language Z is based on classical
(two-valued) logic and set theory.
It has been used in a number of industrial projects,
Similarly, we may specify the operations
for the $Bounded::Counter$ by including
the corresponding operations
specified for the Counter, adding conditions
if required.
From a schema we may easily extract the
pre-conditions for an operation by removing from
the conditions the parts involving
a primed variable.
Clearly, the post-condition is then
characterized by the conditions thus eliminated.
For example, the pre-condition of the
$Counter::Incr$
operation is $v? \geq 0$, whereas
the post-condition is $n' = n + v?$
which corresponds to the implementation requirement
that the new value of the Counter
is the old value plus the value of the
argument $v?$.
In a similar way, the pre-condition
for applying the $Bounded::Incr$ operation
is $n + v? \leq Max$.
Note, however, that this pre-condition
is stronger than the original
pre-condition $v? \geq 0$, hence to conform
with the rules for refinement
we must specify what happens when
$ n + v? > Max $ as well.
This is left as an exercise for the reader.
Clearly, although Z lacks a notion
of objects or classes, it may conveniently
be employed to specify the behavior
of an object.
In
In contrast to Eiffel, which offers only
a semi-formal way in which to specify the behavior
of object classes, Z allows for
a precise formal specification of
the requirements a system must meet.
To have the specification reflect
the object structure of the system
more closely, one of the extensions
of Z mentioned above may be used.
An example of using (plain) Z
to specify the functionality
of a library system is given below.
For
Note that lending a book involves
both the invocation of $book::borrow$
and $person::allocate$.
This could easily be simplified by extending the function
$book::borrow$ and $book::_return$
with the statements $p->allocate(this)$
and $p->deallocate(this)$ respectively.
However, I would rather take the opportunity to illustrate the use
of events, providing a generic solution
to the interaction problem noted.
The Borrow event class provides a controlled
way in which to effect the borrowing of a book.
In a similar way, a Return event class may be defined.
Events are primarily used
as intermediate between the user (interface)
and the objects comprising the library system.
For the application at hand, using events
may seem to be somewhat of an overkill.
However,
events not only give a precise characterization of
the interactions involved but, equally importantly,
allow for extending the repertoire
of interactions without disrupting
the structure of the application
simply by introducing additional event types.
(C) Æliens
04/09/2009
Bounded counter
The specification of a library
State
Operations
Abstract systems and events
User actions may require complex interactions
between the objects constituting
the object model of a system.
Such interactions are often of
an ad hoc character in the sense that they embody one
of the many possible ways in which the functionality
of objects may be used.
What we need is a methodology or paradigm that
allows us to express these interactions in a concise
yet pragmatically amenable way.
In Abstract systems -- design methodology
Events -- high level glue
Example -- the library
Abstract system -- exemplary interface
public:
person* borrower;
book() {}
void borrow( person* p ) { borrower = p; }
void _return( person* p ) { borrower = 0; }
bool inlibrary() { return !borrower; }
};
public:
person() { books = new set example
book* ChandyMisra = new book();
book* Smalltalk80 = new book();
person* Hans = new person();
person* Cees = new person();
Stroustrup->borrow(Hans);
Hans->allocate(Stroustrup);
ChandyMisra->borrow(Cees);
Cees->allocate(ChandyMisra);
Smalltalk80->borrow(Cees);
Cees->allocate(Smalltalk80);
Events
public:
virtual void operator()() = 0;
};
public:
Borrow( person* _p, book* _b ) { _b = b; _p = p; }
void operator()() {
require( _b && _p ); // _b and _p exist
_b->borrow(p);
_p->allocate(b);
}
private:
person* _p; book* _b;
};
public:
Return( person* _p, book* _b ) { _b = b; _p = p; }
void operator()() {
require( _b && _p );
_b->_return(p);
_p->deallocate(b);
}
private:
person* _p; book* _b;
};