The Object Constraint Language: Getting Your Models Ready for MDA (2nd Edition)
Another feature of a user -defined type that you can use within OCL is derived from the associations in the class model. Every association has a number of association ends. Each end has a multiplicity, a type to which it is connected, and an optional ordering marker; it may also have a name. This name is called a rolename . Conceptually, an association end is or defines a feature of the class connected to the other end(s) of the association. Association ends can be used to navigate from one object in the system to another. Therefore, the conceptual features that they define are sometimes called navigations. If the name of the association end is missing, the name of the connected type may be used. If using the typename results in an ambiguity, the specification of a rolename is mandatory. The same is true for associations that are marked to be aggregations. In Figure 8-1, which shows part of the Royal and Loyal model, Customer has two navigations: programs and cards . Class CustomerCard has one navigation named owner. Class LoyaltyProgram also has one navigation named customer. Figure 8-1. Navigations
Navigations in OCL are treated as attributes. The dot-notation used to reference attributes is also used to reference navigations. In one type, all names, whether attribute names or navigation names , must be unique. This arrangement prevents ambiguities between attribute and navigation names. The type of a navigation is either a user-defined type or a collection of user-defined types. If the multiplicity of the association end is at most one, the result type is the user-defined type connected to the association end. If the multiplicity is greater than one, the result is a collection. The elements in the collection must all be of, or conform to, the user-defined type connected to the association end. In our example, the result type of the owner navigation from CustomerCard is a user-defined type: Customer . The result type of both the navigations programs and cards from Customer are collections; in this case, (unordered) Sets . If the association end had been marked ordered, the result type would have been OrderedSet. The differences between sets, ordered sets, bags, and sequences are described in Section 9.1. For the diagram in Figure 8-1, we can define an invariant that uses the owner navigation from the context of CustomerCard . Because this OCL expression results in a value of type Customer, all attributes and operations on Customer that are visible to the contextual instance are now available for use in the remainder of the expression, as shown in the following example: context CustomerCard inv : self.owner.dateOfBirth.isBefore( Date::now ) The same holds for navigations defined on the Customer type that are visible to the contextual instance; they may be used in the remainder of the expressions as well: context CustomerCard inv : self.owner.programs->size() > 0 The result of navigating more than one association with multiplicity many is by definition a value of type Bag . If one of the navigations in the series is marked {ordered} , the result is a value of type Sequence . When we combine navigations, we have the means to navigate through the complete class diagram. From the context of one class in the class diagram, we can write constraints on all connected classes. Surely, this is not good practice. The question of when and how to use navigations is addressed in Section 3.10.2. 8.2.1 Association Classes
A UML class diagram enables us to define association classes , which are classes attached to an association. From an association class, you can always navigate to the instances of the classes at all ends of the association, using the same rules for naming as for normal navigations, for binary as well as multiple assocations. Note that, because of the nature of an association class, such a navigation always results in one single value and never in a collection of any kind. The R&L model (see Figure 8-2) has one association class: Membership . This class has three navigations: programs of type LoyaltyProgram, participants of type Customer , and ”because of the extra association ” currentLevel of type ServiceLevel . The following invariant states that the actual service level of a membership must always be a service level of the loyalty program to which the membership belongs: context Membership inv : programs.levels->includes( currentLevel ) Figure 8-2. Association class from the UML diagram
It is also possible to navigate in the other direction: from the associated classes to the association class. In the R&L model, we can navigate from Customer and LoyaltyProgram to Membership . Because an association class cannot have a rolename, the navigation name is the name of the association class. Note that the previous version of OCL required that the class name be written starting with a lowercase letter. In version 2.0, the name used should be identical to the name of the association class. The multiplicity on the association ends is used to determine the type of the expression. If from a certain context the multiplicity on the opposite end is more than one, then the navigation to the association class will result in a collection of association class instances. If the multiplicity is not greater than one, then the navigation to the association class will result in one association class instance. For example, navigating from Customer to Membership will result in a value of type Set(LoyaltyProgram) . The following invariant makes a statement similar to the preceding one, but from the context of the LoyaltyProgram : the set of service levels must include the set of all actual levels of all memberships. Note that from the context of LoyaltyProgram, the expression Membership.currentLevel is of type Bag(ServiceLevel) ; therefore, we use the operation includesAll instead of includes : context LoyaltyProgram inv : levels->includesAll( Membership.currentLevel ) 8.2.2 Qualified Associations
In a UML class diagram, you can use qualified associations . Qualified associations can be used in OCL expressions in the same way that normal associations are used. The only difference is that we need a way to indicate the value of the qualifier in the expression. The syntax used for qualified associations is object.navigation[qualifierValue, ...] If there are multiple qualifiers, their values are separated by commas. You can navigate to all associated objects by not specifying a qualifier. This is identical to navigation of normal associations. Figure 8-3 shows an alternative class diagram for the R&L model. The ordered association from LoyaltyProgram to ServiceLevel is replaced by an association with the qualifier levelNumber . This means that for each combination of a LoyaltyProgram and a levelNumber, there is zero or one ServiceLevel . The levelNumber specifies the order of the ServiceLevels. To specify that the name of the ServiceLevel with levelNumber 1 must be 'basic' , we can write the following invariant: context LoyaltyProgram inv : self.levels[1].name = 'basic' Figure 8-3. Qualified association in the UML diagram
If we want to state that there is at least one ServiceLevel with the name 'basic' , disregarding the levelNumber , we can state the following invariant: context LoyaltyProgram inv : self.levels->exists(name = 'basic') The first part, self.serviceLevel , is the collection of all ServiceLevels associated with the LoyaltyProgram . The exists operation states that at least one of those service levels must have its name attribute equal to 'basic' . |