constraintsumlclass-diagramocl

Write OCL restrictions related to associations with other classes


How do you model the following restriction?

A place is popular if it has been bookmarked by 2 or more than 2 users.

Here the corresponding uml diagram:

enter image description here uml

I tried several ways, for example:

context place inv: place.popular = (self.user.place.popular>=2)

but nothing worked well...


Solution

  • The constraint that you expressed is an interesting start but does not work because self.user in the place context is a collection. Your expression moreover attempts to use popular as if it were an integer.

    If there would be an unambiguous user, you’d just need to check its size():

    context place inv: 
      place.popular = (self.user->size()>1)
    

    Unfortunately, there are two associations with User: one for the favorites (bookmarks) and for for historic (past visits whether they appreciated or not). This makes that expression ambiguous. To disambiguate, in absence of a role name (name at the association end), you’ll need to qualify the user with the association name:

    context place inv: 
      place.popular = (self.favorites::user->size()>1)
    

    (Btw, in case of absence of association name, you'd need to use the default name A_place_user instead of favorites).

    See also section 7.5.3 of the OCL specifications for more information about navigating associations.

    Edit: more complex navigations**:

    You could also navigate to properties of associated classes. It works like the navigation above, but with the help of the collect() operation. You can then perform collection oprations such as sum()

    context route inv: 
      self.totalDistance = self.step->collect(distanceFromPreviousStep)->sum()
    

    Navigating to a specific object in a collection of linked objects is more delicate. In the case of the steps, we see that the association is ordered (by the way, it should be {ordered} and not «ordered»). This allows to use last() to get the last element in the given order:

    context route inv: 
      self.destination::place = self.step->last().place