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:

  1. 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.

  2. 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.

  3. 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.

  4. 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:

  1. 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.

  2. 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.