Complex constraints involve multiple objects and their links, and capture more complex rules than are shown in the previous section on simple constraints. Multiplicity constraints are a special case of complex constraints in that they have their own notation; you do not need to capture multiplicity constraints using the OCL.
Multiplicity notation is used in Figure 10-1s class diagram to define multiplicity rules that must be respected. One such constraint states that a project relates to a single organization and to a single team. Figure 10-3 shows an object diagram that is invalid because it violates those rules.
The UML's multiplicity notation captures rules regarding the number of objects that may be related to one another. However, using the OCL, many other complex rules may be expressed using constraints.
Figure 10-1 shows the details about organizations, projects, teams, and people, but does not indicate whether a project and its team must relate to the same organization, nor does it indicate whether a team and its members must relate to the same organization. Using the OCL, it's possible to create a complex constraint that enforces the rule that a project, its team, and all the team members must belong to one organization.
Figure 10-4 shows a project and a team that relate to one another and to two different organizations. This would allow a team to work on a project for another organization, which most organizations don't allow! Let's look at how we can use the OCL to prevent the situation in Figure 10-4 from occurring.
There are multiple expressions to show that a project and a team that relate to one another must relate to the same organization.
Within the context of a team, I can use the following expression to assert that the project's organization must match the team's organization:
self.project.organization = self.organization
Coming at it from the other direction, within the context of a project, I can use the following expression to express the same rule:
self.team.organization = self.organization
Within these expressions, the keyword self is optional and the expressions on either side of the = symbol may be interchanged. For example, the following two expressions produce identical results:
self.team.organization = self.organization self.organization = self.team.organization
Keep in mind that if you use less than (<) or greater than (>) in an expression, you cannot interchange the expressions on either side of the operator.
Figure 10-5 shows how the rule is captured in Figure 10-1.
Now consider how these rules can be expressed in the context of an organization. The following rule ensures that every team related to an organization's project is related to the same organization:
organization.project->forAll (p : Project | p.team.organization = self)
The following rule ensures that every project related to an organization's team is related to the same organization:
organization.team->forAll (t : Team | t.project.organization = self)
Notice that the self keyword is not optional in these two expressions because it is the only way for an organization object to refer to itself.
Figure 10-6 illustrates another potential problem, showing a team and a person that relate to one another but that also relate to two different organizations. This allows a person to work on a team for another organization, which most employers don't allow! Thus, we reasonably might want to write a constraint prohibiting the situation.
Consider how a team and a person relate to one another. For example, a team and a person that relate to one another ought to relate to the same organization. Within the context of a team, I can use one of the following expressions to capture this rule between teams, people, and their organization:
self.member->forAll (m : Person | m.organization = self.organization) self.person->forAll (p : Person | p.organziation = self.organization)
Notice that because there are many members of a team, I used the forAll in this expression to indicate that the rule must be satisfied by each element of the set of team members.
Within the context of a person, I can use one of the following expressions to capture this rule between teams, people, and their organization:
self.team.organization = self.organization self.team.organziation = self.employeer
Again, within these expressions, the keyword self is optional and the expressions on either side of the = symbol may be interchanged. You cannot interchange expressions on either side of the < or > symbols.
The multiplicities discussed in the previous section and the rules discussed in this section make the model more precise in representing how organizations, projects, teams, and people are related to one another.