|
|
Printer Friendly Version
The
Contract Micro-Architecture
|
Manual implementation of coordination contracts, or tools such
as the CDE, require an underlying micro-architecture (design
pattern). Apart from the general benefits of using design
patterns, such an architecture, accompanied with implementation
guidelines, is necessary for three additional reasons. Firstly,
it acts as a blueprint for the implementations of coordination
contracts. Secondly, it ensures that such implementations
obey the intended semantics. Thirdly, it provides an abstract
description of tool generated contract-based code.
In
what follows, we present the basic requirements for such a
micro-architecture, together with the basic assumptions that
apply. Finally, we describe and analyse the architecture
on which the CDE generated implementation of contracts is
based. We conclude this document by discussing some implementation
issues based on our experience in implementing the pattern
in Java and C++.
We
must stress the fact that the micro-architecture presented
herein is only one among several possible alternatives for
implementing coordination contracts. There may well be other
solutions, possibly even simpler or more flexible. Therefore,
proposals for a different architecture or different implementation
of contracts are welcome as long as they adhere to the general
principles of contracts as discussed in the Concepts
and Methodology section and summarised in the following
paragraph. If you would like to make alternative suggestions
that fit these requirements, we promise to make them publicly
available at http://www.atxsoftware.com/CDE/patterns.
Send your proposals to cde@atxsoftware.com
The main requirements of the pattern can be divided into two
categories: general architecture requirements and “low-level"
design requirements. The former refer to the general pattern
architecture while the latter are more related to code design
issues that draw from the scope and semantics of contracts.
In
this context, the general architecture requirements are the
following:
-
As already explained, the aim of contracts is to provide
the ability to coordinate the behaviour of software components.
This is achieved by having contracts “intercepting"
calls to components and superposing forms of behaviour that reflect the business rules that apply in the current state.
In addition, contracts can react to state changes
(events) occurring in the state of the participants by superposing the same kind of reaction. Therefore,
two distinct requirements of the pattern are implied. Firstly,
the pattern must provide a means of delegating to contracts
the requests made on the components, while ensuring isolation
between the components and the coordination part (contracts),
i.e none of the components can be aware that coordination is taking
place. Secondly, the pattern must either explicitly allow
or not prevent (during implementation) contracts to be able
to detect conditions or events on components and take react accordingly.
- The proposed architectural solution must provide the required
functionality by minimising the number of required changes to the
original components. In other words, ideally, no changes to the
components would be necessary but, if this is not feasible, changes should
be limited to aspects not related to the
implementation of the component interfaces, thus allowing easier
reengineering of existing applications by not "breaking"
existing interactions.
- The scope and goals of coordination contracts require the
pattern to provide the ability to add and delete contracts in a
“plug and play" mode.
- The pattern should allow for contracts to be able to
effectively coordinate components that are not “stand-alone" i.e
that are subclasses of other components or, generally,
participate in inheritance hierarchies.
The “lower-level" design requirements are the
following:
- Satisfy
the semantics of contracts. This implies that the pattern
and its implementation must allow for the correct instantiation
of contracts (invariant satisfaction) and for the correct
sequence of execution of the synchronisation set. Moreover,
it is necessary to implement a contract exception handling
mechanism, throwing and handling exceptions when an action
involved in the contract fails or a constraint under “with"
is not valid.
- Optimise
performance. The pattern should introduce the minimum possible
number of additional calls and its implementation should
be intelligent enough to minimise a negative impact on performance.
The basic concepts of coordination contracts make
it necessary to state some assumptions that make
development easier and ensure a better understanding of the pattern’s
intended functionality. Firstly, because contracts are used for
controlling the communication between components, they need not
coordinate the participants’ internal operations. Therefore, the
pattern will not deal with internal operations of the components.
Secondly, the components are of a form that allows them to be
coordinated as described. For instance, the preconditions of their
operations are not “hard-wired" inside the code. This implies that
the components’ implementations provide computation features only.
If this is not the case, there will be limitations to the range of
different contracts that can be superposed and a re-engineering phase
that results in components of a suitable form is required.
The Coordination Contract Design Pattern consists
of two parts: The component part and the coordination part. The former
consists of the features that have to be provided for each component
so that it can become coordinated by a contract. The latter concerns
the mechanisms that allow for the coordination of a given component
through the contracts that are in place for it. The classes that
participate in the proposed pattern are shown in the Figure below.

The detailed
functionality of the various classes is
as follows:
Component
Part
- SubjectInterface.
It is an abstract class (type) that defines the operations under
potential coordination. In fact, it is the common interface of
services provided by SubjectToProxyAdapter
and ISubjectProxy.
- Subject.
This is the real component, candidate for coordination,
that provides the concrete implementation of the various
services and inherits from SubjectToProxyAdapter.
- SubjectToProxyAdapter.
Defines the ability to, alternatively, use a proxy or
internal methods for the implementation of a given Subject interface. It is a concrete class that allows, at run time,
and using the polymorphic entity proxy, for delegating received
requests to ISubjectProxy
in the case in which Subject is under coordination. Such
requests are then delegated to ISubjectPartner that links the subject to the contracts that coordinate
it. If no contract is involved, SubjectToProxyAdapter
may forward requests directly to Subject.
In order to achieve this, two actions are necessary. Firstly,
Subject inherits
from SubjectToProxyAdapter.
Secondly, the operations of Subject
are renamed in such a way that the operations with the initial
names are moved to SubjectToProxyAdapter
as concrete operations, and the new operations occurring
in SubjectToProxyAdapter are abstract operations. For instance, operation(
) of Subject exists now as operation()
in SubjectToProxyAdapter and as _operation(
) in Subject.
Moreover, _operation()
is also declared as an abstract operation in SubjectToProxyAdapter.
Requests for operation()
are made to Subject.
However, due to renaming, the operation does not, in fact,
exist in Subject but
in SubjectToProxyAdapter
from which Subject inherits. In the case that
there is no contract (no proxy) involved, operation()
in SubjectToProxyAdapter
forwards the request to the corresponding real
implementation,
_operation(), in Subject. Otherwise, as already stated above, it delegates the request
to ISubjectProxy.
- ISubjectProxy.
It represents an object with the capability of implementing the Subject
interface. It is an abstract class that defines the common
interface of Subject and ISubjectPartner. The interface is inherited from SubjectInterface
to guarantee that all these classes offer the same interface as Subject
with which real subject clients have to interact.
Coordination
Part
- ISubjectPartner.
Defines the general abilities of a
concept to be under coordination. It maintains the connection
between the real object (Subject)
and the contracts in place for it. The class is responsible
for delegating received requests to CtSubjectConnectors
according to a
chain of responsibility. The class contains operations
for managing the chain of responsibility. Alternatively,
the required management operations can be included in an
abstract class, ContractPartner,
from which ISubjectPartner
inherits. However, this is rather a “low-level” design
issue and therefore such a class is not presented in the
pattern.
- Ct_i_SubjectConnector.
A partner that represents the specificities of Subject coordination
for a given contract in which Subject is a participant.
For each pair contract-partcipant there is exactly one Ct_i_SubjectConnector.
The class implementation may be responsible for the execution
of the rules defined in the coordination part of the contract
and for ensuring satisfaction of the contract semantics.
- Contract-i.
A coordination object that defines the rules that will be
superimposed on Subject.
It
should be noted that, naturally, in cases of inheritance
between objects that are under coordination, some additional
classes and some new associations may exist that are not presented
in the previous general architecture. In what follows, we
discuss some “lessons learned" from our experience in
implementing the architecture in Java and C++.
Clearly,
a variety of different “low-level" design decisions can
be made when implementing the general architecture outlined
above. However, such decisions should always take into account
the requirements presented in the beginning of this section.
In this context, there are four issues we wish to discuss:
- The
first issue that should be considered is related to the
implementation of Subject
in Java. As explained above, Subject
inherits from SubjectToProxyAdapter.
The question is, what if Subject
has to inherit from another class, for instance SuperSubject,
given that Java does not support multiple inheritance? In
that case, a good solution is for Subject
and SubjectToProxyAdapter
to be merged into a single class, Subject,
that provides the functionality of both the previous classes.
It should be noted that having SubjectToProxyAdapter
as a Java
interface would not provide a solution because the dynamics
of the pattern rely on call delegations that are defined
in the method implementations of SubjectToProxyAdapter.
In contrast, multiple inheritance is supported in C++ and,
therefore, a C++ implementation is not concerned with such
issues.
- Contracts
should be independently and explicitly created so that they
can be added and deleted in a “plug and play" mode.
A good solution is to explicitly create each contract parameterising
its constructor with the contract’s participants (Subjects).
The contract is then responsible for creating its CtSubjectConnector
passing as argument itself and the participant to which
the CtSubjectConnector
is associated. The CtSubjectConnector
simply checks whether it is the first partner in the
chain of delegation. If it is the first partner it sets
the subject proxy to point to itself, otherwise it adds
itself as the last partner.
- In
cases of inheritance, implementations of the pattern maintaining
a single chain of responsibility will encounter the problem
of having a “non-uniform" chain of responsibility.
By “non uniform" we mean the fact that the chain can
contain a mixed sequence of the different connectors of
the base and the subclasses objects. The chain of delegation
management should be responsible for dealing with this situation
by retrieving and delivering partners that comply with the
corresponding Subject
interface for the trigger being processed.
- Correctly
retrieving the contracts’ rules and executing the contracts’
synchronisation set
are additional issues that have to be considered when
implementing the pattern. There are different strategies
that can be adopted to deal with such issues. For instance,
having a more centralised management of the execution of
the contracts rules versus managing the execution in ISubjectPartner.
©
Copyright 2001 ATX Software SA. All rights reserved.
|
|
|
|
|
|
|