The Object Constraint Language: Getting Your Models Ready for MDA (2nd Edition)

More information can be added to the model in the form of invariants. An invariant is a constraint that should be true for an object during its complete lifetime. Invariants often represent rules that should hold for the real-life objects after which the software objects are modeled .

2.3.1 Invariants on Attributes

A reasonable rule for every loyalty program of R&L would be to require that every customer who enters a loyalty program be of legal age. In the model, this means that the attribute age of every customer object must be equal to or greater than 18. This can be written as an invariant:

context Customer inv ofAge: age >= 18

Invariants on one or more attributes of a class can be expressed in a very simple manner. The class to which the invariant refers is the context of the invariant. It is followed by a boolean expression that states your invariant. All attributes of the context class may be used in this invariant.

2.3.2 The Type of the Attribute Is a Class

When the attribute is not of a standard type, such as Boolean or Integer , but its type is a class itself, you can use the attributes or query operations defined on that class to write the invariant, using a dot notation. For example, the class CustomerCard contains two attributes validFrom and goodThru of type Date . A simple but useful invariant on these two date attributes states that validFrom should be earlier than goodThru :

c ontext CustomerCard inv checkDates: validFrom.isBefore(goodThru)

This invariant uses the operation isBefore in the Date class that checks whether the date in the parameter is later than the date object on which the operation is called. This results in a boolean value. Note that you may use only operations that do not change the value of any attributes; only the so-called query operations are allowed.

2.3.3 Invariants on Associated Objects

Invariants may also state rules for associated objects. The rule that every cardholder should be of age could also be stated as follows :

c ontext CustomerCard inv ofAge: owner.age >= 18

Stating an invariant on associated objects is done by using the rolename on the association to refer to the object on the other end. If the rolename is not present, you should use the name of the class. Note that the previous version of OCL required that the class name be written starting with a lowercase letter. In version 2, the name used should be identical to the name of the association class. Giving rolenames to each association end is preferred. Using rolenames to traverse an association to get to the corresponding instances is called navigation .

Finding the right context for an invariant can sometimes be challenging. Usually, the right context is the context for which the invariant can be expressed in the simplest way and/or for which the invariant is simplest to check.

2.3.4 Using Association Classes

Association classes may also be used in OCL expressions, but some special rules apply. Association classes may not have a rolename; therefore, the name of the association class is used to refer to an instance of that class. The previous version of OCL also required that the class name be written starting with a lowercase letter. In version 2, the name used should be identical to the name of the association class. For instance, the following invariant uses the association class Membership to state that the service level of each membership must be a service level known to the loyalty program for which the invariant holds:

context LoyaltyProgram inv knownServiceLevel: levels-> includesAll(Membership.currentLevel)

Using the association class as context, you may navigate to the instances of both classes at the end of the association to which the association class belongs, using their rolenames:

context Membership inv correctCard: participants.cards->includes(self.card)

Because an association class is in fact a class itself, it can have ordinary associations with other classes. These may be referenced in the manner explained in Section 2.3.3. For example, the following defines a query operation to the association class Membership using the ordinary association between Membership and ServiceLevel :

context Membership def : getCurrentLevelName() : String = currentLevel.name

2.3.5 Using Enumerations

In a UML model, enumeration types may be defined. An enumeration type may be used, for instance, as the type of an attribute of a class in a UML class model. The values of an enumeration type are indicated in an OCL expression by the name of the enumeration type, followed by two colons, followed by the valuename. An example can be found in the CustomerCard class, where the attribute color can have two values, either silver or gold . The following invariant states that the color of this card must match the service level of the membership:

context Membership inv levelAndColor: currentLevel.name = 'Silver' implies card.color = Color::silver and currentLevel.name = 'Gold' implies card.color = Color::gold

Категории