4.4 Implementing OCL Expressions


Once you have decided how to implement the model elements and the OCL standard library, you are ready for step 3, as described in Section 4.1. You now need to consider the implementation of the OCL expressions themselves . Although this looks rather straightforward, the following sections describe some issues that need careful attention.

4.4.1 Evaluation Order

Evaluation order is not (and does not need to be) defined in OCL; it is perfectly legal to write a constraint and have part of that constraint undefined (see Section 10.6). For example, consider the following invariant in the R&L model:

  context  Membership  inv  : account.points >= 0 or account->isEmpty() 

Intuitively, this might be translated into the following fragment of Java code:

 (this.getAccount().getPoints() >= 0) (this.getAccount() == null) 

If there is no account , the sub-expression account.points >= 0 evaluates to undefined in OCL, and the sub-expression account->isEmpty() evaluates to true. In OCL, the result of the full invariant is true and well defined. However, if you generate code directly from the invariant as described, and the execution order is left to right, the this.getAccount().getPoints() code will try to reference a nonexisting object ( account ). Usually, this leads to a runtime error (e.g., java.lang.nullPointerException ), so care must be taken to avoid such situations.

In the Java code, you need to add a test to determine whether certain objects exist. In the preceding example, you should write the following:

 ( (this.getAccount() == null) (this.getAccount().getPoints() >= 0) 

In this piece of Java code, the nullPointerException can never occur.

4.4.2 No Side Effects

An OCL expression should always be treated as an atomic expression. No changes of value of any object in the system can take place during the evaluation of the expression. In purely sequential applications, this might not be a problem, but in a parallel, multi- user environment, it must be addressed. The values referenced in a single OCL expression should be visible and reachable from the thread that executes the implementation of the expression.

4.4.3 Getting Attribute and AssociationEnd Values

The code for getting an attribute value depends on the way in which attributes from the UML model are mapped to Java. In the mapping described in Section 4.2, the value of an attribute named attribute can be obtained through the operation getAttribute() . Therefore, any attribute reference in OCL needs to be mapped to the corresponding get operation. For instance, the OCL expression

 self.attribute 

is transformed into the following Java code:

 this.getAttribute(); 

The Java code to get the value of an association end depends on the mapping of the UML association in the same way. The solution in Section 4.2 maps an association end to a Java private attribute with a corresponding get operation. This means that the OCL navigation of an association will become an operation call in Java. For instance, the OCL expression

 self.associationEnd 

is transformed into the following Java code:

 this.getAssociationEnd(); 

If another implementation for attributes or associations is chosen , then the implementation of the OCL expressions should follow this.

4.4.4 Let Expressions

In OCL let expressions, local variables are defined. Likewise, a let expression can be implemented by defining a local variable. In the next example, the operation selectPopularPartners selects program partners that have generated numerous large transactions after the given date. Its postcondition is stated in terms of the transactions generated by these partners :

  context  LoyaltyProgram::selectPopularPartners( d: Date )                                                : Set(ProgramPartner)  post: let  popularTrans : Set(Transaction) =           result.deliveredServices.transactions->asSet()  in  popularTrans->forAll( date.isAfter(d) ) and           popularTrans->select( amount > 500.00 )->size() > 20000 

This expression can be implemented using a local variable in the separate operation that implements the postcondition (see Section 4.5.2). Note, of course, that the scope of the local variable must be at least equal to the scope of the OCL expression in which the let variable is used.

4.4.5 Treating Instances as Collections

In OCL, an object can be treated as a collection, as in instance->size() . In programming languages, this is not very common. The simplest way to translate such an expression into Java is to introduce a temporary collection that holds the instance as the only element. For instance, the following OCL expression from the context of Membership uses a LoyaltyAccount object as collection:

 account->size() 

It can be implemented by the following Java code. Again, the parts of the OCL expression that can be recognized are printed in boldface:

 Set coll = new HashSet(); if( account != null ) coll.add(  account  ); return coll.  size  (); 

The check for account != null is necessary to ensure that we never put the null value in the created set. If we did, the result of the Java code would be incorrect. Another option to implement this specific OCL expression is to determine whether the object reference is equal to null, as shown in the following code:

 if( account != null ) return 1; else return 0; 

This option results in simpler Java code, but is less general, because it implements the size operation in a specific manner.



Object Constraint Language, The. Getting Your Models Ready for MDA
The Object Constraint Language: Getting Your Models Ready for MDA (2nd Edition)
ISBN: 0321179366
EAN: 2147483647
Year: 2003
Pages: 137

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net