10.3 Complex Constraints

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.

Figure 10-3. Invalid relationships between organizations, a project, and teams
figs/Luml_1003.gif

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.

You could capture multiplicity constraints using the OCL, but instead you should use the notation for such constraints already built into the UML.

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.

Figure 10-4. Relationships between organizations, a project, and a team
figs/Luml_1004.gif

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.

Figure 10-5. Organizations, projects, teams, and people with another OCL expression
figs/Luml_1005.gif

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.

Figure 10-6. Relationships between organizations, a team, and a person
figs/Luml_1006.gif

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.