Enhancing the Object Constraint Language for ... - Semantic Scholar

8 downloads 0 Views 62KB Size Report
influenced by the work of Cook and Daniels [2] which bor- rows heavily from Z. ..... A. Hamie, F. Civello, J. Howse, S. Kent, and R. Mitch- ell, “Reflections on the ...
Enhancing the Object Constraint Language for More Expressive Specifications Ali Hamie Division of Computing University of Brighton Lewes Road, Brighton, UK +44 1273 642032 [email protected]

ABSTRACT The Object Constraint Language (OCL) is a precise language which could be used for describing constraints on object-oriented models and other modelling artifacts. The kind of constraints which can be described using OCL include invariants on classes, types and interfaces, preconditions and postconditions of operations and methods. This paper describes some enhancements to OCL that make specifications convey information more effectively. For example, adding redundant invariants, timebased constraints, postconditions, and examples could make the specification clearer to readers, and can be used in showing that the specification says what is intended. Another example of an enhancement is the use of case analysis sugar which splits up a specification into manageable parts. These enhancements have been already integrated in some specification languages such as Larch/ C++, a larch style behavioral interface specification language for C++. Keywords UML, OCL, constraints, specification, modelling 1 INTRODUCTION The Object Constraint Language (OCL) [10][21][24][25] is a specification language for describing constraints on object-oriented models. It is an optional part of the Unified Modelling Language (UML) [22][5] which is the de facto standard for object-oriented analysis and design. OCL allows constraints on a UML model to be expressed, that can not be easily expressed using standard diagrammatic notations. Specifically, OCL supports the expression of invariants on classes and types, preconditions and postconditions on operations, allowing the modeller to specify pre-

cise and detailed constraints on the behaviour of a model, without getting embroiled in implementation details. Kent [11] has developed a diagrammatic notation based on Venn diagrams for describing constraints which could be used in conjunction with UML/OCL. OCL is the culmination of recent work in OO modelling [2][4] which has selected ideas from formal methods to combine with diagrammatic, object-oriented modelling resulting in a more precise, robust and expressive notation. Syntropy [2] extended OMT [23] with a Z-like textual language for adding invariants to class diagrams and annotating transitions on state diagrams with preconditions and postconditions. However, OCL adopts a simple non-symbolic syntax and restricts itself to a small set of core of concepts. This paper describes some enhancements to OCL which would make specification convey information effectively. One enhancement is to allow the specification of redundant information which could provide a basis for debugging specifications. That is by adding some information which should be implied by the specification would help readers and help check that the specification says what is intended. These enhancements apply generally to the precondition and postcondition approach to specification [13] and they have been integrated with other specification languages such as Larch/C++ [14][15] which is a Larch style behavioral interface specification language for C++, and JML (Java Modelling Language) a behavioral interface specification language for Java [12]. The main contribution of this paper is to incorporate these enhancements into OCL for specifying UML object-oriented models. We extend OCL with time-based constraints. We also extend the enhancement of adding redundant specifications with the ability to express equivalent forms of invariants and postconditions. The paper is organised as follows. Section 2 is an introduction of using OCL, in combination with a kernel of the UML diagrammatic notation (class diagrams), in writing

invariants, preconditions and postconditions. Section 3 introduces redundant specifications into UML/OCL models. Section 4 discusses time-based constraints and the introduction of redundant constraints within OCL. Section 5 deals with case analysis. Section 6 provides a conclusion and further work. 2 THE OBJECT CONSTRAINT LANGUAGE The Object Constraint Language (OCL) [10][21][24][25] is a specification language for describing constraints on object-oriented models. It is developed at IBM and it is an optional part of the object-oriented de facto standard UML. It is based on textual rather than symbolic syntax which makes it more accessible for specifying constraints on object-oriented models than other specification languages such as Z [20] and VDM [9]. The design of OCL is heavily influenced by the work of Cook and Daniels [2] which borrows heavily from Z. The constraints which are expressible using OCL are as follows: •

Invariants on Classes or Types that must hold at all times.



Preconditions which are constraints that must hold before the execution of an operation.



Postconditions which are constraints that will hold after the execution of an operation under the appropriate precondition.



Guards which are constraints on the transitions of an object from one state to another.

The constraints are described in the context of an objectoriented model, that is they cannot be stand alone constraints. The following example illustrates the use of OCL in describing some of these constraints. 2.1

Example Model

The example we shall use is a class diagram describing a model for a hotel management system. This example is based on the example in [25]. A hotel has a number of rooms that can be rented to guests. There are also a number of bathrooms, which are either associated with a specific room or used by multiple rooms on the same floor. The model is expressed in UML which consists of a set of notations for describing object-oriented models. A full description of UML can be found in [22] and a distilled description can be found in [5]. The class diagram introduces a language of classes, attributes and associations. This language automatically becomes part of OCL and can be used to form expressions. For example, the class names introduced by the class diagram Hotel, Room, etc. become types in OCL. The attribute names such as address, minFloor, maxFloor, etc. also become part of the vocabulary of OCL. Association roles such as hotel, guests, etc. also become part of OCL.

{numberOfRooms>=1}

Hotel address: String numberOfRooms:Integer minFloor: Integer maxFloor: Inetger

hotel

totalRent(): Real

room

paint(newColor: Color) 0..1

hotel

0..1

guests

* Guest

name: String age : Integer sex: {male, female}

Room floorNumber: Integer roomNumber:Integer rooms numberOfBeds: Integer rent: Real * color : Color

BathRoom * guests

floorNumber: Integer roomNumber:Integer usage: Integer uses(g: Guest)

Figure 1. Class diagram for hotel model These as we shall see later can be used to form navigation expressions. 2.2

Invariants

The constraints which can be expressed using OCL are always connected to a UML object-oriented model. An invariant is a constraint which can be associated with a class, type or interface in a UML model. The invariant is expressed as a boolean expression which restricts or limits the value of an attribute or association role, or it can state a relationship between the values of attributes and association roles. The result of the expression must be true for all instances of the associated class at any point in time. A simple invariant on the hotel model is that the number of rooms in a hotel is greater than or equal to one. Such an invariant can be shown in the class diagram as a text between curly brackets in a note box with a dotted line to the associated class as shown in Figure 1. The standard UML stereotype can be used to indicate an invariant constraint. However, in some cases invariants tend to take up too much space resulting in a cluttered diagram. This is the case when there are a large number of invariants. This can be overcome by writing invariants separately in a

text document. For example, the invariant on the class Hotel can be expressed in OCL as follows: context Hotel invariant: self.numberOfRooms >= 1

The keyword context specifies the context of the invariant which is the class Hotel in this case. self is a keyword in OCL which represents a contextual object for which the constraint applies. In this case self refers to an object of class Hotel. In OCL, self can be omitted because the contextual object is the default. So the above invariant could also be expressed as: context Hotel invariant: numberOfRooms >= 1

2.3

Preconditions and Postconditions

The behavior of an operation of a class or type can be precisely specified in terms of a precondition and postcondition pair. The precondition and postcondition are expressed using OCL expressions. The name self can also be used in the precondition or postcondition referring to the object on which the operation was invoked. Expressions occurring in a postcondition can refer to two sets of values for each property of an object: • the value of a property at the start of the operation or method. • the value of a property upon completion of the operation or method. In OCL the value of a property at the start of the operation is denoted by postfixing the property name with the commercial sign @, followed by the keyword pre. As a simple example, consider the specification of the operation increaseRent in terms of precondition and postcondition pair which is given below. This operation increases the rent of a given room by an amount taken as a parameter to the operation. context Room:: increaseRent(amount : integer) pre: amount > 0 post: rent = rent@pre + amount

The keyword context indicates the context in which the operation can be applied which is the class Room in this case. The precondition states that the amount must be positive. The postcondition states that the new rent of the room has been increased by amount. 3 REDUNDANT SPECIFICATIONS A redundant specification describes properties which should follow from the main specification. As such, a

redundant specification does not form a part of the main specification, rather it can be regarded as a formalised commentary on it. Adding redundant parts to a specification results in a more expressive specification language. That is expressive in the way it conveys information or constraints to the reader. This would allow modellers and specifiers to express properties the model should have, and which are important to readers without cluttering up the main specification. Another important reason for adding redundant specifications is the ability to check the main specification. For instance, a reader would be able to check his or her understanding of the specification against the redundant parts. The approach of adding redundant parts of a specification has been incorporated in the Larch family of specification languages [8]. The emphasis was on the benefits of checking that a specification meets the specifier’s intuition and intention by comparing the redundant parts against the main parts; such checking is called “debugging” a specification [6]. For example, the Larch Shared Language (LSL) [8] has features which makes it possible to state redundant claims about theories. In this section we incorporate features into OCL/UML that can be used to state redundant claims and properties about UML models. This would include adding redundant invariants, redundant preconditions and postconditions. 3.1

Redundant Invariants

A class or type may have several invariants constraining its objects. The combination of these invariants describes constraints relating attributes and association roles. Some of the consequences of these relationships could be stated as redundant invariants. In OCL one way to introduce a redundant invariant is to have a keyword redundant indicating a redundant property of an object of a given class or type. The following illustrates this: context Class redundant Invariant: redundantInvariant

Where Class is a class which provides the context of the redundant invariant redundantInvariant. This states that redundantInvariant should follow from the combination of all the invariants on Class. As an example consider the following simple invariant on the class Hotel: context Hotel invariant: self.numberOfRooms >= 1

Which states that the number of rooms in a hotel is greater or equal to one.

Another invariant on the Hotel class is the following which relates the attribute numberOfRooms to the association role rooms: context Hotel invariant: self.numberOfRooms = self.rooms->size

This invariant says that the number of objects in the room collection (rooms->size) is equal to the value of the attribute numberOfRooms. Given these two invariants we could state the following derived or redundant invariant: context Hotel redundant invariant: self.rooms->size >= 1

This redundant invariant states that the number of objects in the room collection is greater than or equal to one. It is easy to see that this follows from the two previous invariants by substituting equals for equals. In order to debug an invariant one is required to prove the following: invariant implies redundantInvariant

Where invariant is the invariant and invariantRedundant is the redundant invariant. With tool support the specification of a class (collection of invariants) could be checked against redundant properties of the class. Another form of redundant information is to add equivalent invariants. In OCL we could use the keyword equivalent to add equivalent invariants. For example an equivalent form of the above invariant is: context Hotel invariant: self.numberOfRooms = self.rooms->size equivalent invariant: self.rooms->size = self.numberOfRooms

This could be used to debug invariants by proving that the equivalent form is equivalent to the main invariants. This has not been incorporated in any specification language. Object states on a state diagram must be related to the attributes and association roles on a class diagram. This can be expressed as an invariant in OCL. To distinguish invariants on states from invariants on attributes one could introduce a keyword state Invariant as follows: context Class state invariant: stateInvariant

Where stateInvariant is the predicate relating the a state to the attributes of Class. This may seem redundant because a state can be represented as an attribute on a class diagram, so that the keyword invariant may be sufficient. However, this might

be useful when reading invariants relating state attributes to other attributes. 3.2

Redundant Postconditions

A redundant postcondition can be introduced when specifying an operation in terms of a precondition and a postcondition pair. As such a redundant postcondition is stated in the context of an operation specification. It describes properties that should follow from the main part of a specification. In the context of UML/OCL redundant postconditions can be introduced by using redundant postcondition keywords to state operation claims. The following illustrates this in general: context Class:: op(p1: T1, ..., pn: Tn) : T pre: precondition post: postcondition redundant post: redundantPost

where precondition is the precondition, postcondition is the postcondition and redundantPost is the redundant postcondition. In order to make use of redundant postconditions for debugging an operation specification, for each such claim, one would try to prove the following: precondition and postcondition implies redundantPost

As an example consider the following specification of the operation paint on the hotel model: context Room::paint(newColor: Color) pre: guests->isEmpty post: color = newColor and rent = rent@pre+1

The precondition says that a room can only be painted when it is empty, i.e. there are no guests in it. While the postcondition says that the room has a new color and that the rent is increased by ten pounds. The specification of paint with a redundant postcondition could be given as follows: context Room::paint(newColor: Color) pre: guests->isEmpty post: color = newColor and rent = rent@pre+10 redundant post: rent = rent@pre+10

This says that the postcondition should imply that the rent is increased by ten per cent, which is easy to see by the property of the boolean operator and. Another useful form of redundancy is to state a postcondition in an equivalent form. This could be achieved by a keyword equivalent post as the following example shows:

context Room::paint(newColor: Color)

context Class invariant:

pre: guests->isEmpty

invariant

post: color = newColor and rent = rent@pre+10

example: example

equivalent post: rent = rent@pre+10 and color= newColor

This example is simple but it illustrates the idea behind equivalent postconditions. To debug a specification with equivalent form one has to show the following: postCondition implies equiPost and equiPost implies postCondition

Where postCondition is the postcondition and equivalent form of the postcondition.

equiPost

is the

Again with tool support one would be able to debug an operation specification. 3.3

Examples

Another form of redundancy to specifications is obtained by adding examples which clearly illustrate what is to be done. In OCL, we could use the keyword example to incorporate examples within specifications. the following example illustrates this:

Where invariant is the invariant and example is the predicate describing the example. This could help debugging invariants by proving the following; example implies invariant

Adding examples to an invariant could be used to show that the invariant is satisfiable. That is there is at least one object that makes the invariant true. 3.4

Redundant Preconditions

It is also possible to add redundant preconditions to an operation specification. This enables one to state properties that must follow from the precondition, which could be useful for exposing to the reader properties that follow from the semantics of the specification language. In OCL redundant preconditions could be introduced by introducing a keyword redundant pre as follows: context Room::paint(newColor: Color) pre: guests->isEmpty

context Room::paint(newColor: Color)

redundant pre: guests->size = 0

pre: guests->isEmpty

post: color = newColor and rent = rent@pre+1

post: color = newColor and rent = rent@pre+10 example: newColor = red and guests = Set{} and color=red and rent@pre = 40 and rent=50

The example shows clearly the relationship between the variables involved in the specification.

The redundant precondition states that the number of guests in the room is zero, which is easily derived from the fact that the guest collection is empty. In addition, if the type Color is specified as an enumerated type such as {red, green, blue, yellow}, then a derived precondition is given as follows: redundant pre: newColor = red or newColor= green or

The specification language Larch/C++ [15] was the first language to incorporate examples as part of interface specifications. Examples can also help in debugging specifications. The specification of the operation describes a relationship between the pre- and post-state. The example describes a pair of states which must be in the relation specified by the operation specification. That is the following must be proved: example implies (precondition implies postcondition)

Where example is the predicate describing the example, preis the precondition and postcondition is the postcondition. This is equivalent to proving the following:

newColor = blue or newColor= yellow

This unpacks the semantics of enumerated types in OCL. For debugging specifications with redundant preconditions one is required to prove the following: precondition implies redundantPre

Where precondition is the precondition and redundantPre is the redundant precondition. It is not clear at this stage whether there is great need for incorporating redundant preconditions into OCL.

condition

(example and precondition) implies postcondition

It is more useful to give examples which are consistent with the precondition. Therefore, it might be useful to check that the predicate obtained from the example is consistent with the precondition. It is also possible to add examples to invariants. This could be achieved as follows:

4 TIME-BASED CONSTRAINTS An invariant on a class or type in OCL must hold for any object of the class in all visible states. A visible state is one that can be observed by clients of that class. Such invariants express common properties that would otherwise be repeated in every operation’s precondition and postcondition. However, invariants are not just mere notational abbreviations, because they apply to all operations, even when new ones are added to a class or type.

Time-based constraints are similar to invariants. However, a time-based constraint on a class or type must hold for any ordered pair of visible states in a computation, where the first state occurs before the second. The property expressed by a time-based constraint must express a reflexive and transitive relation on states in order to make sense. As for invariants, time-based constraints if not expressed separately, would otherwise have to be repeated in every operation’s postcondition. However, time-based constraints are not just mere notational abbreviations, because they apply to all operations, even when new ones are added to a class or type. The idea of time-based constraints was first introduced in [16][17] under the name history constraints. Timebased constraints can be easily incorporated in OCL by introducing a keyword constraint as follows: context Class constraint: constraint

where constraint is a boolean expression representing the constraint. This expression relates two states (before state, after state) and will involve @pre. A simple time-based constraint is that some attributes of an object never changes its value once it is initialized. For example, in the hotel model one constraint is that the floorNumber of a room never changes. In OCL this could be expressed as: context Room constraint:

As a simple example, suppose that the number of rooms in a hotel does not change. This can be expressed as a timebased constraint as follows: context Hotel constraint: self.numberOfRooms = self.nmberOfRooms@pre

We also have the following invariant: context Hotel invariant: self.numberOfRooms = self.rooms->size

A simple example of a redundant time-based constraint is given in the following: context Hotel redundant constraint: self.rooms->size = (self.rooms->size)@pre

Which is derived easily from the invariant and the timebased constraint by following simple rules. That is if self.numberOfRooms is constant and self.rooms->size equals to self.numberOfRooms, then it is easy to see that self.rooms->size is also constant. To show this, from the invariant we have: (self.numberOfRooms = self.rooms->size)@pre = (self.numberOfRooms = self.rooms->size)

By the properties of the operator @pre we have: (self.numberOfRooms = self.rooms->size)@pre = (self.numberOfRooms@pre) = (self.rooms->size)@pre

Here the operator @pre is applied to a boolean expression, with the rule that (b1=b2)@pre is equal to (b1@pre = b2@pre).

floorNumber = floorNumber@pre

This only applies to objects which are not newly created. That is the object must exist in at least two visible states.

From this and the time-based constraint we have:

Time-based constraints can also be used to express monotonic relationships between pre-states and post-states. For example, in the hotel model, a guest age increases with time. This can be expressed as follows:

In general, if an attribute f is related only to an attribute g, and g is a constant attribute, then it follows that f is also constant. Other interesting situations would arise with monotonic constraints and invariants. This example shows the interaction between invariants and time-based constraints for deriving properties about the model.

context Guest constraint: age >= age@pre

In order to debug a time-based constraint we could incorporate redundant constraints using redundant constraint keywords: context Class redundant constraint: redundantConstraint

Where redundantConstraint is the redundant constraint. Debugging time-based constraints would involve proving the following constraint implies redundantConstraint

Note that constraint could be a conjunction of time-based constraints.

self.rooms->size = (self.rooms->size)@pre

Another idea which could be adopted from Larch/C++ is to limit a time-based constraint so that it only applies to some operations rather than all operations [3]. This can be used to group in one place common parts of the postconditions of several operations. This idea was inspired by that advocated by Borgida et al., for dealing with frame axioms [1]. 5 CASE ANALYSIS Case analysis is a syntactical mechanism which can be used for decomposing a specification into more manageable parts. This syntactic sugar was first introduced by Wing [27] to split up a specification into several cases. This was also reinvented by Wills [26] and used in the object-oriented development method Catalysis [4].

As an example consider the specification of an operation increaseRent which increases the rent of a room depending on season: context Room::increaseRent(season: {L, H}) pre: (season = L or season = H) post: season = L implies rent = rent@pre +10 and season = H implies rent = rent@pre +30

The precondition states that season is either low (L) or high (H). This actually is a derived (redundant) precondition since it is implied by the semantics of enumerated types. However, it is included to illustrate specifications with cases. The postcondition states that if the season is low then the rent is increased by ten units, and if the season is high the rent is increased by 30 units. This actually show a specification with two cases. Using the case analysis sugar this could be expressed in OCL as follows: context Room::increaseRent(season: {L, H}) pre: season = L post: rent = rent@pre +10 also pre: season = H post: rent = rent@pre +30

The sugared form of the specification is more readable than the other form because it shows each case clearly. The case analysis sugaring is more effective when a specification involves several cases with each case corresponding to a long expression. Another reason is that OCL expressions tend to be long because of the use of textual syntax rather than symbolic syntax. Another important reason is that an operation specification may have several specifications which have to be composed. For example, a part of the specification may come from a state diagram, while another part may come from the class diagram. Case analysis would allow a textual representation of the operation specification which mirrors the cases from the state diagram. 6 CONCLUSION This paper has described several enhancements to the Object Constraint Language for expressing various constraints on UML object-oriented models. These enhancements apply generally to the specification technique which is based on preconditions and postconditions, and have already been incorporated within other specification languages such as Larch/C++. These enhancements contribute to the expressiveness of the OCL which could help modellers communicate more effectively with potential clients and implementers.

One of the enhancement which could prove effective in practice is that of case analysis sugar. This helps breaking up a specification into more manageable and easily understood parts. However, to be sure of the effectiveness of these enhancements, experimental tests are needed. Another significant enhancement is that of adding examples to specifications. This can help debug specifications and make specifications clearer. Future work may involve the incorporation of frame axioms within UML/OCL specifications.

ACKNOWLEDGEMENTS I am grateful to my colleagues at the University of Brighton for useful discussions.

REFERENCES 1. A. Borgida, J. Mylopoulos, and R. Reiter, On the Frame Problem in Procedure Specifications, IEEE Transactions in Software Engineering, 1995; Vol. 21, No. 10. 2. S. Cook, and J. Daniels, Designing Object Systems: Object-Oriented Modelling with Syntropy, PrenticeHall, Hemel Hempstead, UK, 1994, p. 389. 3. K. Dhara and G. Leavens, Forcing behavioral subtyping through specification inheritance, In Proceedings of the 18th International Conference on Software Engineering, IEEE Computer Society Press, 1996. 4. D. D'Souza, and A. Wills, Objects, Components and Frameworks with UML: The Catalysis Approach, Addison-Wesley, UK, 1998, also available at http:// www.trireme.com/catalysis. 5. M. Fowler, and K. Scott, UML Distilled, Addison-Wesley, 1997, p. 179. 6. S.J. Garland, J. Guttag, and J.H. Horning, Debugging Larch Shared Language specifications, IEEE Transactions on software Engineering, 16(6):1044-1057. 7. J. Guttag, and J. Horning, Larch: Languages and Tools for Formal Specifications, Springer-Verlag, 1993. 8. J. Guttag, J. Horning, S. Garland, K. Jones, A. Modet, and J. Wing, Larch: Languages and Tools for Formal Specifications, Springer-Verlag, New York, N. Y., 1992. 9. C. Jones, Systematic Software Development using VDM (2nd edition), Prentice-Hall, Hemel Hempstead, UK, 1990, p. 333. 10. A. Kleppe, J. Warmer, and S. Cook, “Informal Formality? The Object Constraint Language and its application in the UML metamodel”, Proc. of UML’98 International Workshop, P. Muller and J. Bezivin, ed., Mulhouse, France, June 3-4, 1998, pp. 127-136.

11. S. Kent, “Constraint Diagrams: Visualising Invariants in Object-Oriented Models”, Proc. of OOPSLA97, ACM Press, 1997. 12. G. Leavens, A. Baker, and C. Ruby, Preliminary Design of JML: A Behavioral Interface Specification Language for Java. Technical Report, TR98-06, Dept. Computer Science, Iowa State University, USA, February 1999. 13. G. Leavens, and A. Baker, Enhancing the Pre- and Postcondition Technique for More Expressive Specifications, Technical Report, TR97-19a, Dept. Computer Science, Iowa State University, USA, September 1997, revised February 1999. 14. G. Leavens, An overview of larch/C++: Behavioral specifications for C++ modules, In Haim Kilov and william Harvey, editors, Specification of Behavioral Semantics in Object-Oriented Information Modelling, chapter 8, pages 121-142. Kluwer Academic Publishers, Boston, 1996. 15. G. Leavens, Larch/C++ Reference Manual, version 5.25, Available in ftp://ftp.cs.iastate.edu/pub/larchc++/ lcpp.gz or on the World Wide Web at http:// www.cs.iastate.edu/~leavens/larchc++, January 1999. 16. B. Liskov and J. Wing, A behavioral notion of subtyping, ACM Transactions on Programming Languages and Systems, 16(6):1811-1841, 1994. 17. B. Liskov and J. Wing, A, Specifications and their use in defining subtypes, ACM SIGPLAN Notices, 28(10)16-28, 1993, OOPSLA’93 Proceedings.

18. A. Hamie, F. Civello, J. Howse, S. Kent, and R. Mitchell, “Reflections on the Object Constraint Language”, Proc. of UML’98 International Workshop, P. Muller and J. Bezivin, ed., Mulhouse, France, June 3-4, 1998, pp. 137-145. 19. S. Stepney, R. Barden, and D. Cooper, editors, Object Orientation in Z, Workshops in Computing, Springer, UK, 1992. 20. M. Spivey, The Z notation (2nd ed.), Prentice Hall, UK, 1992. 21. Rational Software Corporation, Object Constraint Language Specification, Version 1.1, http://www.rational.com, 1997. 22. Rational Software Corporation, The Unified Modeling Language, Version 1.1, http://www.rational.com, 1997. 23. J. Rumbaugh, M. Blaha, W. Premerali, F. Eddy, and W. Lorensen, Object-Oriented Modelling and Design, Prentice-Hall, Emglewood Cliffs, New Jersy, 1991, p. 500. 24. Warmer, J., and Kleppe, A., The Object Constraint language: Precise Modelling with UML. Addison-Wesley, 1999. 25. Warmer, J., and Kleppe, A., OCL : The Constraint Language of the UML. JOOP, May, 1999. 26. A. Wills, Specification in Fresco, In Stepney et al [19], chapter 11, pages 127-135. 27. J. Wing, A two-tiered approach to specifying programs, Technical Report TR-299, Massachusetts Institute of technology, laboratory for Computer Science, 1983.

Suggest Documents