• Ei tuloksia

Implementation Language Extensions and Pattern Formalisms

Chapter 4 Tool Support for Object-Oriented Frameworks

4.1 Framework Modeling Techniques

4.1.5 Implementation Language Extensions and Pattern Formalisms

An interesting approach to describe framework characteristics is to enable direct support for frameworks on the programming language level. Although this is much lower level support than that of cookbooks or other high-level notations, it is also more accurate and offers attractive possibilities for supporting framework construction and specialization with automated tools. Language level support usually means some tagging techniques for marking design pattern roles and framework hot spots, or a contract mechanism for restricting framework speciali-zation. Tagging is usually quite straightforward and could be implemented, for instance, similarly to Java’s formal comments. The idea of contracts is, however, more complex and also more powerful.

A software contract captures mutual obligations and benefits among interacting components. Contracts are used for strengthening and deepening interface speci-fications. A common way of specifying software contracts is to use Boolean as-sertions called pre- and postconditions for each service offered, as well as class invariants for defining general consistency properties. For instance, a contract for service suppliers and clients could state the following: a client should ask a sup-plier for a service only in a state in which the class invariants and the precondi-tions of the service are respected. In return, the supplier promises that the work specified in the postconditions will be done and that the class invariants will still

be respected. In this way, the rights and obligations of both the client and the supplier are clearly profiled.

Within the OO paradigm, the notion of formal contract was introduced in [Helm et al. 1990] (see also [Holland 1992]). A contract is a formal programming-language-like description that specifies and restricts the properties of behavioral compositions. Behavioral compositions are groups of interdependent objects that cooperate to accomplish a particular task. The idea of Helm et al. was to provide a formal basis for a new paradigm called interaction-oriented design which was aimed at the abstraction and reuse of these behavioral compositions. In [Helm et al. 1990], the authors present a set of language extensions to specify, manipulate, and apply contracts.

A contract specification identifies the participants in a behavioral composition.

All participants have a number of contractual obligations, consisting of type obli-gations and causal obliobli-gations. The type obliobli-gations describe the instance vari-ables and the external interface the participant must support. The causal obliga-tions define the acobliga-tions that must be performed in response to messages.

Figure 4.1 shows a simplified version of the Observer pattern [Gamma et al.

1994] as a UML class diagram and as a contract definition. The pattern captures a collaboration where an arbitrary number of observer objects get notified when a particular event takes place on the model that the observers listen to. The Ob-server contract has two participants: Subject and ObOb-servers. ObOb-servers is a set of Observer objects. The supports clause specifies the interface for a participant.

The interface for Subject contains three causal obligations: attach for adding ob-servers to the subject, detach for removing them, and notify for broadcasting an update request to all the observers. Each causal obligation may be annotated with postconditions that must be met. For example, attach has a condition {o ∈ Ob-servers} denoting, that after the operation is performed, the Observer given as a parameter must be included in the Observers set. A causal obligation may also have resulting actions associated with it. For instance, after calling notify, the up-date operation is called for all the observers.

Finally, the invariant section specifies conditions that shall be constantly main-tained by the participants and the instantiation section takes care of setting up the initial conditions for the contract.

Contracts can be specialized by contract refinement. A contract is refined by ei-ther specializing the type of a participant, by adding participants, by extending its actions, or by defining a stronger invariant. All obligations that are not overrid-den are inherited from the supercontract. Contracts may also be composed of simpler contracts via the inclusion mechanism. The included contracts put addi-tional obligations on the participants defined in the contract. The mappings be-tween the participants in the including contract and in the included contract must be explicitly named. The obligations in the included contracts cannot be overrid-den, as is the case with refinement. Note that the contract refinement and inclu-sion mechanisms are analogous to subclassing and composition in OO languages, respectively.

Figure 4.1: The Observer pattern [Gamma et al. 1994] as a UML class diagram and as a formal contract [Jézéquel et al. 2000]

Actual classes are mapped to contract participants through conformance declara-tions. The mapping is not necessarily one-to-one since the instance variables and methods that together satisfy the obligations of a particular participant may be distributed over several classes in an inheritance hierarchy. Furthermore, the naming of participants and classes can be different.

After contracts have been specified and mapped to class implementations, they are ready for use. Using a contract means creating a behavioral composition within an application. Behavioral compositions are created by instantiating tracts. This involves allocating objects for the participants. Obviously, the con-formance declarations play a major role in this process. The instantiation sections of the different contracts are used to initialize the compositions.

Contracts make it possible to explicitly specify behavioral compositions such as frameworks and patterns. From the specifications, real object compositions can be generated and monitored. Monitoring, of course, requires dedicated tool sup-port. A major concern with formal approaches like this is that they require such a detailed specification that it is very hard to apply them to other than small com-positions. We argue that automated tool support can relax this concern also.

Pre- and postconditions along with class invariants were first implemented in the Eiffel language [Meyer 1992a, 1992b] under the name design by contract. Varia-tions of the contract mechanism are available as language extensions in a number of general-purpose programming languages and OO modeling techniques. Ex-amples of these include iContract [Kramer 2000] and CoffeeStrainer [Bokowski 1999] for Java, and Object Constraint Language (OCL) [Warmer-Kleppe 1999]

for UML.

The problem with using general-purpose languages to describe design patterns is that the languages are usually not powerful enough to represent a model for a pat-tern. Instead they represent its instance. Therefore, in order to reuse the pattern, the instance of the pattern has to be copied and modified to fit the new context.

Regardless of this shortcoming, the use of (language-based) constraints for re-stricting the specialization of frameworks is a very promising approach, if only the constraints for application classes could be specified in the abstract classes that exist inside the framework.

contract Observer Subject supports [

attach(o: Observer) α {o ∈ Observers}

detach(o: Observer) α {o ∉ Observers}

notify() α <∀ o: o ∈ Observers: o → update()>]

Observers: Set(Observer) where each Observer supports [ update()]

invariant instantiation

<∀ o: o ∈ Observers: Subject → attach(o)>

end contract

Besides language extensions and contracts, also various other formalizations have been proposed for patterns and framework hot spots. Such formalizations are discussed, for example, in [Florijn et al. 1997], [Meijler et al. 1997], [Mikkonen 1998], and [Riehle 2000]. Eden et al. propose a logic-based approach, namely a formal language called LePUS [Eden et al. 1999; Eden-Grogono 2001]

for describing and reasoning about OO software architectures, designs, and pat-terns. LePUS specifications can be expressed as either formulas or semantically equivalent graphical diagrams.