Interconnecting Objects via Contracts

Share Embed


Descripción

Interconnecting objects via contracts Luís Filipe A. Andrade OBLOG Software S.A. Alameda António Sérgio 7 – 1 A, 2795 Linda-a-Velha, Portugal [email protected]

José Luiz L. Fiadeiro LabMAC – Dept of Informatics Faculty of Sciences, University of Lisbon Campo Grande, 1700 Lisboa, Portugal [email protected]

Abstract. The evolution of today's markets and the high volatility of business requirements put an increasing emphasis on the flexibility of systems, i.e. on the ability for systems to accommodate the changes required by new or different organisational needs with a minimum impact on the implemented services. In this paper, we put forward an extension of UML with a semantic primitive – contract – for representing explicitly the rules that determine the way object interaction needs to be coordinated to satisfy business requirements, as well as the mechanisms that make it possible to reflect changes of the business requirements without having to modify the basic objects that compose the system. Contracts are proposed as extended forms of association classes whose semantics rely on principles that have been used in Software Architectures and Distributed System Design for supporting dynamic reconfiguration.

1

Introduction

Market evolution, and the consequent evolution of business requirements, make the corresponding problem domains to become larger and more complex. As a result, the information systems that are required to support business activities are becoming more sophisticated and complex too. Such complex systems can be seen as large collections of coarse-grained parts with a high degree of interaction. From an objectoriented point of view, they are best seen as collections of interacting, concurrent and distributed objects that are responsible for keeping information and performing the required business activities. Market evolution and volatility of business requirements have a very deep influence on organisations and their information systems. More and more, large organisations face two important problems in this respect: • How should information systems be conceived and developed in order to support the continuous evolution of the core business and the evolution of information system technology? • How to make information system development and maintenance scalable in the context of highly volatile business application domains?

–1–

When systems are conceived as collections of interacting objects, it seems clear that the problems that we have just identified require that we be able to express, and make available as first-class citizens, the constraints and the rules that capture the business requirements of the application domain. Because business rules determine the way object behaviour and interaction needs to be coordinated, it is necessary that these coordination aspects be available explicitly in the system models so that they can be changed, as a result of modifications that occur at the level of the business requirements, without having to modify the basic objects that compose the system. That is, we should make the evolution of the system compositional with respect to the evolution of the application domain in the sense that changes occurring in the domain should be mapped directly to the architecture of the information system. These observations result from the experience that Oblog Software has accumulated in developing banking applications, an area in which fierce competition has dictated the need for services to be easily reconfigured. For instance, new account packages are defined almost every other day to attract more customers and prevent old customers from being lured by the new services offered by competitors. Time-to-market is a business decision that can be severely conditioned by the capacity of the bank's information system to support and accommodate the definition of new types of package with minimal impact on the applications that manage the individual accounts. To a certain extent, the modelling of interactions between objects can be made explicit through what in UML is known as association classes. Indeed, association classes allow us to represent, in conceptual models, interactions as first-class citizens, i.e. at the same level of importance as the objects themselves. An example can be given, using the banking example mentioned in the previous paragraph, in terms of the interaction that is required between customers and accounts. For that purpose, an association class ownership can be used that, as a class, has attributes and methods that model the activities associated with the ownership. The fact that account packages come in all colours and flavours can be captured by the existence of more than one ownership class between classes account and customer. For instance, besides the standard package, we can well envisage a VIP-ownership association class with an attribute credit that establishes the amount by which the account can be overdrawn. Association classes are somewhat too generous in the way they promote relationships to the status of objects, namely in what concerns interfacing with classes external to the association, which may hinder the kind of flexibility that we have motivated above. We shall discuss such issues in the body of the paper. The main problem, however, lies in the mechanisms that are usually made available for implementing associations because they do not provide the degree of flexibility that is required by the need to create, delete or modify such classes without having to change the implementation of the roles. For instance, a solution that requires the code of account to be redefined and/or recompiled each time a new account package is put on the market is not acceptable. Hence, the usual implementation of associations in terms of attributes does not provide this degree of flexibility. Our purpose in this paper is to propose a new primitive for object-oriented modelling — contracts — which is an extension of association classes at the representation level as discussed above, but relies on implementation mechanisms that ensure the

–2–

degree of flexibility required by the need to reflect changes in the business rules. Our solution capitalises on previous work on Software Architectures [11] and Configurable Distributed Systems [9], and relies on a modularisation technique – superposition – developed for Parallel Program Design [7] that we show to be implementable in current component-based techniques like CORBA and Java Beans. In section 2, we further motivate the concept of contract through the analysis of different models of the bank account package. In section 3, we propose a textual notation for and give examples of the application of contracts. In section 4, we outline a semantics for contracts that is based on categorical mechanisms previously used for software architectures. Finally, in section 5, we outline an implementation for contracts as a design pattern that ensures the levels of flexibility motivated above.

2

Motivation

Let us resume the discussion on account packages so that more motivation for the envisaged notion of contract can be provided before we start formalising the concept. In order to achieve the degree of flexibility that one wants from contracts, namely the ability to define new interactions or modify existing interactions without changing the partners in the contract, it is essential that interactions be promoted to first-class citizens. That is to say, it is necessary that interactions be provided with class properties and features like their own attributes and methods. An example for this need can be given in terms of the method withdrawal(amount) of account. A customer that owns an account has, typically, an attribute owns:account so that transactions performed by that customer can be modelled through calls of the form owns.withdrawal(a). The condition balance≥amount is typically declared on account as a precondition on withdrawal(amount) to ensure that there are enough funds for the withdrawal to be made. This precondition establishes a contractual relationship between account and customer, both in the technical sense developed by B.Meyer [10] and as one of the business rules that establish the way customers are required to interact with accounts. In order to understand the problems raised by the need to reconfigure systems as a reaction to the constant evolution of market rules, assume that it becomes necessary to make certain instances of the interaction between customers and accounts more flexible by creating "VIP-packages" according to which accounts can be overdrawn up to an amount agreed in advance between the customer and the bank. A naïve solution to the problem of adapting the existing system to the new business rules would be to enrich account with a new operation — VIP-withdrawal — for the more flexible withdrawals. A disadvantage of this solution is in the additional burden that needs to be placed on the supplier — the account — to decide when the new operation is to be used. The typical OO-solution to this new situation is different: it consists in defining a subclass VIP-account of account with a new attribute credit and a weaker precondition on withdrawal(amount): balance+credit≥amount. In this way, the more flexible contractual relationships can be established directly between the

–3–

client (customer) and the specialisation of the supplier (the VIP-accounts). Nevertheless, there are two main drawbacks in this solution. On the one hand, it introduces, in the conceptual model, classes that have no counterpart in the real problem domain. It is the customers who are VIPs, not the accounts. However, having placed the contractual relationship between customers and accounts in account, one is forced to model the revised contract through a specialisation of the previous one, which implies the definition of the artificial subclass of account. The second disadvantage is not methodological but a technical one. The new solution still requires changes to be performed on the rest of the system because the other classes need to be made aware of the existence of the new specialised class so that links between instances can be established through the new contract. The disadvantages in both solutions result from the fact that the contract between customers and accounts is being placed on the side of the supplier (the account), which is what is favoured by Meyer's notion of contract. This makes it difficult to accommodate new forms of the contract that depend on the client side of it. Hence, it makes more sense to place the contract at the level of the relationship that exists between customers and accounts by promoting it to what in UML is known as an Association Class, i.e. an association that can also have class properties and features. In the account package example, the desired promotion could be achieved by introducing an association class ownership in which the details that pertain to the envisaged coordination of the joint behaviour of clients and accounts, including the preconditions that apply to the interactions, would be placed. Customer

Account

Ownership

Changes to the ownership contract, such as the addition of the attribute credit and the weaker precondition on the interaction, can now be put on specialisations of the new class without having to change the role classes. Indeed, resorting to association classes keeps the model faithful to the application domain by representing explicitly the business rules that coordinate the interaction between the entities involved. As a consequence, changes in the business rules should be more easily accommodated in the model without having to modify the classes that model the entities involved and, hence, leave a vast core of the information system unchanged. From the discussion held above about the disadvantages of the attribute-based representation of the relationship between accounts and customers, it seems clear that the best way of implementing the contract through the association class is for a new operation to be declared for ownership that can act as a mediator between the roles.

–4–

Upon a call from the client, the mediator would have the responsibility of determining whether the contractual relationship between the partners is valid and, in case it is, delegate on the supplier to proceed. In this way, it is, indeed, possible to achieve the required flexibility for accommodating changes in the business rules simply by modifying the contracts as required, e.g. at the level of the preconditions of the mediators, without having to modify the partners in the contract. However, things are not as clean with this solution as they may seem. On the one hand, the fact that a mediator is used for coordinating the interaction between two given objects does not prevent direct relationships to be established that may violate the contract. In the case of the account package, nothing prevents a designer from connecting directly a customer to an account, possibly breaching the contract because the precondition has now been moved from the account to the mediator. On the other hand, the solution is not incremental (or additive) in the sense that the addition of new business rules cannot be achieved by simply introducing new contracts. Indeed, different contracts may interact with each other thus requiring an additional level of coordination among the mediators themselves, i.e. the introduction of contracts between contracts, etc. This leads to models that are not as abstract as they ought to be due to the need to make explicit (even program) the relationships that may exist between different contracts. The notion of contract that we put forward in this paper circumvents these problems by relying on a mechanism of superposition that is more powerful than the use of mediators as outlined above. It provides a coordination role that is closer to what is available for configurable distributed systems and software architectures in general. The idea is that, from a static point of view, a contract defines an association class as discussed above, but the way interaction is established between the partners is different from what is available between ordinary objects. In the next sections, we will propose a syntax, semantics and implementation for contracts. Before that, we summarize the intuitive semantics of this new primitive: • first of all, the partners in the contract are not even aware that they are being coordinated by a third party; instead of interacting with a mediator that then delegates execution on the supplier, the client calls directly the supplier; however, the contract "intercepts" the call and superposes whatever forms of behavior are prescribed; this means that it is not possible to bypass the coordination being imposed through the contract; • the same transparency applies to all other clients of the same supplier; no changes are required on the other interactions that involve either partner in the contract; hence, contracts may be added, modified or deleted without any need for the partners, or their clients, to be modified as a consequence; • the effect of superposing a contract is cumulative; because the superposition of the contract consists, essentially, of synchronous interactions, and synchronisation is transitive, different contracts will superpose their coordinating behavior, achieving a cumulative effect; in fact, an algebra of contracts can be defined much in the same way as the algebraic operations on architectural connectors defined in [12].

–5–

3

Notation and examples

The notation that we adopt for contracts is the one made available in OBLOG, a UML-compliant language that is being defined, together with associated tools, by Oblog Software [www.oblog.pt]. Constructs in OBLOG have a textual and a graphical notation. The graphical notation that has been chosen for contracts is reminiscent of UML association classes: the basic difference is that the rectangle of the association class has been replaced by a scroll.

Customer

Account

Ownership

Given an application of a contract to relevant classes of a system (the partners in the contract), the instances of the partners that can actually become coordinated by instances of the body can be subject to conditions. The typical case is that the instances are required to be related through some association between the partners. Such conditions are specified as invariants in the contract body. In OBLOG, the textual format of contracts is as follows: contract partners invariant constants attributes operations coordination behaviour // the contract's own behaviour end contract

Each interaction under "coordination" is of the form : when do with

The name of the interaction is necessary for establishing an overall coordination among the various interactions and the contract's own actions. This is similar to what happens in parallel program design languages like Interacting Processes [4]. The condition under "when" establishes the trigger of the interaction. Typical triggers are the occurrence of actions in the partners. The "do" clause identifies the reactions to be performed, usually in terms of actions of the partners and some of the contract's own

–6–

actions. Together with the trigger, the reactions of the partners constitute what we call the synchronisation set associated with the interaction. Finally, the "with" clause puts further constraints on the actions involved in the interaction, typically further preconditions. The intuitive semantics (to be formalised in the following sections) is that, through the "when" clause, the contract intercepts calls to the partners or detects events in the partners to which it has to react. It then checks the "with" clause to determine whether the interaction can proceed and, if so, coordinates the execution of the synchronisation set. All this is done atomically. An example can be given through the account packages already discussed. The traditional package, by which withdrawals require that the balance be greater than the amount being withdrawn, can be specified as follows: contract Traditional package roles x : Account; y : Customer; invariants ?owns(x,y)=TRUE; coordination tp: when y.calls(x.withdrawal(z)) do x.withdrawal(z) with x.Balance() > z; end contract

Notice that, as specified by the invariant, this contract is based on the ownership association previously discussed. This contract involves only one interaction. It relates calls placed by the customer for withdrawals with the actual withdrawal operation of the corresponding account. The customer is the trigger of the interaction: the interaction requires every call of the customer to synchronise with the withdrawal operation of the account but enables other withdrawals to occur outside the interactions, e.g. by other joint owners of the same account. The constraint is the additional precondition already discussed. Notice that the constraint applies only to the identified pair of customer and account, meaning that other owners of the same account may subscribe different contracts (e.g. the VIP-contract already discussed). The notation involving the interaction in this example is somewhat redundant because the fact that the trigger is a call from the customer to an operation of the account immediately identifies the reaction to be performed. In situations like this, OBLOG allows for abbreviated syntactical forms of interaction. However, in the paper, we will consistently present the full syntax to make explicit the various aspects involved in an interaction. In particular, the full syntax makes it explicit that the call put by the client is intercepted by the contract and the reaction, which includes the call to the supplier, is coordinated by the contract. Again, we stress that such interactions are atomic, implying that the client will not know what kind of coordination is being superposed. From his point of view, it is the supplier that is being called. In general, we allow for contracts to have features of their own. The examples below illustrate some of these situations. The first example addresses the VIP-package already discussed. This contract requires a constant and an attribute for establishing the intended coordination.

–7–

contract VIP package partners x : Account; y : Customer; constants CONST_VIP_BALANCE: Integer; attributes Credit : Integer; invariants ?owns(x,y)=TRUE; x.AverageBalance() >= CONST_VIP_BALANCE; coordination vp: when y.calls(x.withdrawal(z)) do x.withdrawal(z) with x.Balance() + Credit() > z; end contract

It is important to stress that features declared for the body are all private to the contract: they cannot be made available for interaction with objects other than the partners. Indeed, the contract does not define a public class. We end this section with an example that illustrates a more involved form of coordination. Consider a package of two accounts of the same customer, a savings account and a checking account, that allows for a more efficient management of the customer's funds. The customer defines the minimum balance he or she wants in its checking account; by doing so, the package automatically transfers funds between the two accounts in order to maintain that amount always available in the checking account and the maximum possible amount in the savings account to earn interest. contract Flexible package partners c : CheckingAccount; s : SavingsAccount; attributes m_minimum : Integer; invariants c.owner=s.owner; coordination stoc: when (c.?GetBalance() < m_minimum) do s.TransferFrom(m_minimum-c.?GetBalance(),c); ctos: when (c.?GetBalance() > m_minimum) do c.TransferFrom(c.?GetBalance()-m_minimum,s); end contract

In this case, the triggers of the interactions are not calls of the partners. It is the contract itself that has the initiative of monitoring the state of the partners and trigger transactions between them.

4

Semantics of contracts

The semantics that we propose for contracts is based on a program design language and model – CommUnity [2] – that is similar to Unity [1] and Interacting Processes [4]. A basic difference between CommUnity and these other languages is that it re-

–8–

places the shared-variables model of interaction by an object-based coordination model centred around private state and shared actions. CommUnity is not a full-fledged object-oriented language but its primitives are expressive enough to capture the aspects that are involved in contracts. In particular, the mechanism that it provides for interconnecting simple programs into complex systems is the one that explains how contracts can be used to coordinate the behaviour of given objects in a system – superposition or superimposition [7]. Templates of object behaviour can be expressed in CommUnity according to the following structure: object P is var output out(V) input inp(V) init Ι [] do g: [B(g) → g∈Γ

|| a∈D(g)

a:=F(g,a)]

where • V is the set of variables. Variables can be declared as input or output and they correspond to communication channels that are used by the object. Input variables are used for receiving data from the environment of the program. They provide a simplified model for data transmission usually associated with input parameters of methods. Output variables provide for both queries in the sense of UML, i.e. observations of the state of the object, and the output parameters usually associated with methods. They are not used as representations of the state of the object but, rather, as abstractions of the state that are convenient for the specification of interactions with the rest of the system. • Γ is the set of action names; each action name has an associated guarded command. Actions represent interactions between the program and the environment. Hence, their execution is also under the control of the environment in the sense that their occurrence may require synchronisation with actions of other objects in the system. Each action g is typed with a set D(g) consisting of the output variables whose values may change as a result of the occurrence of action g. For every variable v, we also denote by D(v) the set of actions whose occurrence can change v. • I is a proposition over the set of output variables – the initialisation condition. • For every action g∈Γ, B(g) is a proposition over the set of variables – its guard. When the B(g) is false, g cannot be executed. • For every action g∈Γ and local variable v∈D(g), F(g,v) denotes the value that v has after the execution of g. As an example, consider the template corresponding to a bank account: : o b j e c t account i s var output balance input amount init do withdraw: [→ balance := balance–amount] [] deposit: [→ balance := balance+amount]

As explained, the input variable amount accounts for the input parameters that are

–9–

necessary for both actions. On the other hand, the output variable balance can be used as a query of the state of the account (no commitment is made about the representation of the state) as used, for instance, in the specification of the flexible package given in the previous section. A model-theoretic semantics of CommUnity is presented in [8] based on labelled transition systems. Consider now the specification of interactions between objects. In previous papers [e.g. 2,3] we have argued in favour of the use of Category Theory as a mathematical framework for expressing system configurations following Goguen's work on General Systems Theory [6]. In such a framework, interconnections are expressed via morphisms. The notion of morphism between programs captures what in the literature on parallel program design is known as superposition. The original idea of superposition is of a mechanism meant to support a layered approach to systems design by which we are allowed to build on already developed components (drawing on the services they provide) by "augmenting" them (by, say, extending their state space and/or their actions/control activity) while preserving their properties. Most of the conditions expressed in the definition below are standard when defining superposition and are more thoroughly discussed in [2]. A program morphism σ:P1 →P2 consists of a (total) function σvar :V 1 →V 2 and a partial mapping σac :Γ2 →Γ1 s.t.: 1. For every v∈V 1 , o∈out(V 1 ), i∈inp(V 1 ), we have Sort 2 (σvar (v))=Sort1 (v), σvar (o)∈out(V 2 ), and σvar (i)∈out(V 2 )∪inp(V 2 ); 2. For every g∈Γ2 s.t. σac(g) is defined and v∈V1 : σvar (D1 (σac (g))⊆D2 (g) and σac (D2 (σvar (v))⊆D1 (v); 3. For every g∈Γ2 s.t.σac (g) is defined and v∈D1 (σac (g)): (F2 (g,σvar (v))= σ(F1 (σac (g),v))); 4. (I2 ⊃ σ(I1 )); 5. For every g∈Γ2 s.t. σac (g) is defined, (B2 (g) ⊃ σ(B1 (σac (g)))) A morphism σ:P1 →P2 identifies a way in which P1 is "augmented" to become P2 so that P2 can be considered as having been obtained from P1 through the superposition of additional behaviour. A typical situation, and the one that interests us for contracts, is when this additional behaviour is given itself as an object, e.g. a regulator for constraining the behaviour of the original object or an observer that monitors given properties of the underlying object. In such situations, superposition is achieved through the interconnection of the two objects. Because of this, when describing superposition, we usually call P1 the component and P2 the system. The map σvar identifies, for every variable of the component, the corresponding variable in the system and σac identifies the action of the component that is involved in each action of the system, if ever. Condition 1 states that sorts, visibility and locality of variables are preserved. Notice, however, that input variables of P1 may become output variables of P2 . This is because the result of interconnecting an input variable of P1 with an output variable of another component of P2 results in an output variable of P2 (the input/output communication is internalised but the values are still made available for communication to the outside). Condition 2 means that the domains of variables are preserved and that an action of the system that does not involve

– 10 –

an action of the component cannot change any variable of the component. Conditions 3 and 4 correspond to the preservation of the functionality of the component program: (3) the effects of the actions have to be preserved or made more deterministic and (4) initialisation conditions are preserved. Condition 5 allows guards to be strengthened but not weakened. Strengthening the guard is typical of superposition and reflects the fact that all the components that participate in the execution of a joint action have to give their permission. System configuration in the categorical framework is expressed via diagrams. Morphisms as in the diagram below can be used to establish synchronisation between actions of programs P1 and P2 as well as the interconnection of input variables of one component with output variables of the other component for data transmission during interaction. channel σ1

σ2

P1

P2

This kind of interaction can be established in a configuration diagram through interconnections of the form depicted above, where channel is essentially a set of input variables and a set of actions. Each action of channel acts as a rendez-vous point where actions from the components can meet (synchronise): any action a of channel establishes the synchronisation set that consists of all pairs of actions of P1 and P2, respectively, that are mapped through the morphisms to a. Hence, action names act as interaction points as in IP [4]. On the other hand, each variable of the channel provides for an input/output communication to be established between the components. This semantics of the configuration diagram is the one that is obtained through the colimit of the diagram. Taking the colimit of a diagram is a categorical construction that collapses the configuration into an object by internalising all the interconnections. In the case of actions, it represents every synchronisation pair of actions of P1 and P2, as identified above, with a single action a1||a2 whose occurrence captures the joint execution of the synchronised actions. The assignments performed by the joint action are exactly the assignments performed locally by each of the synchronised actions; its guards are the conjunctions of the guards of the components, i.e. B(a1||a2)=B(a1)∧B(a2). This is how a contract superposes new forms of behaviour. See [2] for more details on the colimit semantics of configuration diagrams. More complex configurations can be described by diagrams of the form channel σ1 P1

channel µ1

µ2 Glue

σ2 P2

where Glue is a program that describes how the activities of both components are coordinated. Such configurations typically arise from the superposition of architectural connectors to given components of the system [3] with the aim of establishing interactions between the components. This is also the way we formalise contracts.

– 11 –

Indeed, consider the general form of contracts as depicted in section 3. Each instance of a binary contract , i.e. a contract with two partners, gives rise to a configuration diagram as above where the Pi are the partners and the Glue is the contract body. The channels identify the interactions specified in the contract under "coordination". More precisely, the name of each interaction gives rise to an action name in Glue. For each interaction a, σi -1(µi (a)) is the action of P i involved in that interaction, i.e. morphisms establish the interactions as claimed above. The different modes in which synchronisation can be programmed in CommUnity for achieving the required interaction are explained in [12]. The conditions of the "with" clause of contracts become guards of the actions that correspond to the interactions. Notice that, as explained through the colimit semantics, the effect of the interactions is to superpose the guards of the actions involved, which includes the "with" conditions. This is how contracts achieve the overall coordination effect. Finally, it is important to stress that the incremental nature of contract superposition is captured in this formal framework through the properties of categorical diagrams. Again, see [12] on this aspect as applied to architectural connectors.

5

A design pattern for contracts

As already explained in the previous sections, a contract works as an active agent that coordinates the contract partners. In this section, we are concerned with the way these coordination mechanisms can be implemented. When defining an implementation, we need to have in mind that, as motivated in the introduction, we should be able to superpose a contract to given objects in a system and coordinate their behaviour as intended without having to modify the way these objects are implemented. This degree of flexibility is absolutely necessary when the implementation of these objects is not available or cannot be modified, as in legacy systems. It is also a distinguishing factor of contracts when compared with existing mechanisms for modelling object interaction, and one that makes contracts particularly suited in business domains where the ability to support the definition and dynamic application of new forms of coordination is a significant market advantage. Different standards for component-based software development have emerged in the last few years, among which CORBA, JavaBeans and COM are the current trend in industry. However, none of these standards provide a convenient and abstract way of supporting superposition as a first-class mechanism. Because of this, we propose our solution as a design pattern. This pattern exploits some widely available properties of object-oriented programming languages such as polymorphism and subtyping, and is based on other well known design patterns, namely the Broker and the Proxy, or Surrogate [5]. The class diagram below depicts the proposed pattern. In what follows, we explain, in some detail, its basic features, starting with the participating classes.

– 12 –

SubjectInterface-2 Request2()

AbstractSubject-2

proxy

Subject-2 proxy.Request2()

contract

Contract contract

Request2()

Request2() broker

Before_Request1() After_Request1() Before_Request2() After_Request2()

PartnerConnector-1

Request1()

implementation

RealSubject-1

Request1() Undo_Request1()

PartnerConnector-2

Request2()

RealSubject-2

Request2() Undo_Request2()

Request2()

implementation if ( contract.Before_Request2() = TRUE ) { implementation.Request2(); if ( contract.After_Request2()=FALSE ) implementation.Undo_Request2(); }

Client

SubjectInterface-i – as the name indicates, it is an abstract class (type) that defines the common interface of services provided by AbstractSubject-i and Subject-i. Subject-i – This is a concrete class that implements a broker maintaining a reference that lets the subject delegate received requests to the abstract subject (AbstractSubject-i) using the polymorphic entity proxy. At run-time, this entity may point to a RealSubject-i if no contract is involved, or point to a PartnerConnector-i that links the real subject to the contracts that coordinate it. AbstractSubject-i – is an abstract class that defines the common interface of RealSubject-i and PartnerConnector-i. The interface is inherited form SubjectInterface-i to guarantee that all these classes offer the same interface as Subject-i (the broker) with which real subject clients have to interact. RealSubject-i – is the concrete domain class with the business logic that defines the real object that the broker represents. The concrete implementation of provided services is in this class. PartnerConnector-i – maintains the connection between contracts and the real object (RealSubject-i) involved as a partner in the contract. Adding or removing other contracts to coordinate the same real object does not require the creation of a new instance of this class but only of a new association with the new contract and an instantiation link with the existing instance of PartnerConnector-i. This means that there is only one instance of this class associated with one instance of RealSubject-i. Contract – is a coordination object that is notified and takes decisions whenever a request is invoked on a real subject. If there are no contracts coordinating a real subject, the contract pattern can be simplified and only the classes that are not dimmed in the figure become necessary. The introduction of a contract implies the creation of instances for the dimmed classes and associations. The following is a possible object diagram when no coordination contract is defined, which means that there are not Contract and no PartnerConnector instances. cl :Client

(1) Request2

s2-broker :Subject-2

(2) Request2

s2 :RealSubject-2

In this scenario, where object s2 is not under coordination, the only overhead imposed by the pattern is an extra call from the broker to the real object. Introducing a new contract to coordinate interaction with the real object s2 implies only modifications on object s2-broker, making its proxy become a reference to object c2 (of type PartnerConnector-2). Doing only this minor modification, neither the clients (e.g. object cl) nor the real object s2 need to be modified in order to accommodate the new behaviour established by adding the contract ct. The new behaviour introduced by contract ct is described in the object diagram below. This diagram shows how the contract superposes a new behaviour when requests of type Request2() are invoked on a real object s2.

– 14 –

cl

ct

(3)

:Contract

:Client

Before_Request2

(1)

(3.1) (5.1)

Request1

(5)

Request2

After_Request2

(5.3) c1

c2

:PartnerConnector-1

:PartnerConnector-2

s2-broker (2) :Subject-2 Request2

(3.2)

(4)

Request1

Request2

s1 (5.2) :RealSubject-1

Undo_Request1

s2

(5.4) Undo_Request2

:RealSubject-2

In this diagram we can see that once Request2() is sent to the s2-broker it delegates its execution on the proxy reference (in this case on c2 instead of s2, as seen on the previous figure). In c2 the implementation of subject services has the following format (2): Request2() is { (3): If ( contract.Before_Request2() = TRUE ) Then { (4): implementation.Request2(); (5): If (contract.After_Request2() = FALSE ) Then implementation.Undo_Request2(); } } That is to say, before the partner connector c2 gives rights to the real object s2 to execute the request, it intercepts the request and gives right to the contract ct to decide if the request is valid and perform other actions. This interception allows us to impose other contractual obligations on the interaction between the caller and the callee. This is the situation of the first model discussed in section 2 where new pre-conditions were established between Account Withdrawals and their Customers. On the other hand, it allows the contract to perform other actions before or after the real object executes the request. Only if the contract authorises can the connector ask s2 to execute and commit, or undo execution because of violation of post-conditions established by the contract. As stated at the beginning of this section, current component-based technology does not provide a convenient way for coordinating components. The benefit of having this form of coordination available has a primitive construction when specifying components and their interactions is that it avoids the burden of having to code such a pattern. In the meanwhile, tools which, like OBLOG, provide automatic code generation from high level specifications, must hide the implementation complexity of coordination, allowing the developer just to specify the contract itself.

– 15 –

6

Concluding remarks

In this paper, we proposed an extension of UML with a new semantic primitive – contracts – for making explicit, in conceptual models, the business rules that coordinate the behaviour of given objects in a system, and allowing for this business rules to be added, or modified, without having to modify the way those objects are implemented. This proposal was made based on the experience that we have had in conceiving and implementing combined packages of banking products. In those packages, contracts emerged as enablers for more flexible domain models, and for the effective reuse of architectural design patterns to implement the system and accommodate changes with minimal impact. A semantics of the new primitive was proposed based on a categorical formalisation of the notion of superposition that has been used in parallel program design for achieving the kind of incrementality in design that motivated the use of contracts. These semantics were previously used for formalising the notion of connector as used in Software Architectures, which further highlights the similarities between the proposed primitive and the mechanisms that have been developed in Software Engineering for Re-configurable Distributed Systems. We also showed how contracts can be implemented with Object Oriented Programming Languages like C++ or Java, by proposing a design pattern that makes it possible to introduce or remove contracts without affecting the objects under coordination. With this solution, only the contract compilation and program linkage is required whenever new contracts or changes to existing ones occur. On the other hand this contract pattern allows us to coordinate third-party components (changing the behaviour of black boxes) without having to redefine or even recompile them. This makes contracts particularly suited for component-based programming. This new primitive is also intended as an extension of B.Meyer's notion of contract [10], awarding it the role of coordinator of object interaction. Indeed, we showed that there are advantages in moving contracts from supplier classes to the association classes (contracts) that capture interconnections, namely as a way of enabling modifications to contracts that reflect specialisations of the client classes. It is our belief that, even if the design solution that we offered is not adopted, there are many advantages in using the proposed primitive at the more abstract modelling levels as we hope was evident from the example that we were able to fit in the paper.

Acknowledgements This work was partially supported by Fundação para a Ciência e a Tecnologia under contract PRAXIS XXI 2/2.1/TIT/1662/95.

– 16 –

References 1. K.Chandy and J.Misra, Parallel Program Design - A Foundation, Addison-Wesley 1988. 2. J.L.Fiadeiro and T.Maibaum, "Categorical Semantics of Parallel Program Design", Science of Computer Programming, 28:111-138, 1997. 3. J.L.Fiadeiro and A.Lopes, "Semantics of Architectural Connectors", in TAPSOFT'97, LNCS 1214, Springer-Verlag 1997, 505-519. 4. N.Francez and I.Forman, Interacting Processes, Addison-Wesley 1996. 5. E.Gamma, R.Helm, R.Johnson and J.Vlissides, Design Patterns: Elements of Reusable Object Oriented Software, Addison-Wesley 1995 6. J.Goguen, "Categorical Foundations for General Systems Theory", in F.Pichler and R.Trappl (eds) Advances in Cybernetics and Systems Research, Transcripta Books 1973, 121-130. 7. S.Katz, "A Superimposition Control Construct for Distributed Systems", ACM TOPLAS 15(2), 1993, 337-356. 8. A.Lopes and J.L.Fiadeiro, "Using Explicit State to Describe Architectures", in Proc. International Conference on Fundamental Aspects of Software Engineering (FASE’99), J-P.Finance (ed), LNCS 1577, Springer-Verlag 1999, 144-160. 9. J.Magee and J.Kramer, "Dynamic Structure in Software Architecures", in 4th Symp. on Foundations of Software Engineering, ACM Press 1996, 3-14. 10. B.Meyer, "Applying Design by Contract", IEEE Computer, Oct.1992, 40-51. 11. M.Shaw and D.Garlan, Software Architecture: Perspectives on an Emerging Discipline, Prentice Hall, 1996. 12. M.Wermelinger and J.L.Fiadeiro, "Towards an Algebra of Architectural Connectors: a Case Study on Synchronisation for Mobility", in Proc. 9th International Workshop on Software Specification and Design, pp. 135-142, IEEE Computer Society Press 1998

– 17 –

Lihat lebih banyak...

Comentarios

Copyright © 2017 DATOSPDF Inc.