- Introduction: What is a loop?
- Mistake 1: Redundant loops
- Mistake 2: Imprecise loops
- Summary
Mistake 2: Imprecise loops
When you tie both ends of a loop together you can easily miss- specify the constraints that determine which instances are allowed to interconnect. Consider a university that requires Students to choose an advisor among Professors who work in the same Department as the Student's declared major. Does the following model satisfy this requirement?
As drawn, Model 7.6 places no restriction on the Student's choice of an advising Professor. This is easier to see if we populate our model with some example instances.
In an unconstrained loop instances may be linked across one association without regard to links along other associations. Model 7.6 works for a university where a Student may select an advisor without regard to whether or not the selected Professor is on the staff of the Student's major Department. But that doesn't match the requirement at our university!
We need to constrain the loop to get the following result:
In this constrained loop, an instance of Student may link across R2 to a subset of Professor instances. The subset of allowable Professor instance selections is defined by links along R1. This would also work if Professors do the selecting. In that case an instance of Professor could link along R2 to one or more instances of Student within a subset defined by an existing link across R3.
In fact, you can start anywhere you like in the loop and the constraint holds. Let's consider an unlikely case just to prove the point. Say that an instance of Professor, Phillip, plans to advise a particular Student, Terrance, in the physics department. So we start with an instance of Professor and we want to link to an instance of Department across R1. If Phillip wants to be Terrance's advisor, he must be hired in the Department selected by tracing the opposite way around the loop R2->R1.
How do we express this constraint on the class diagram? First we must define the loop. We can do this with an expression like R1+R2+R3. Secondly, we need to say that the loop is of the constrained variety.
Model 7.6 is annotated below to reflect the constraint on the loop.
In Model 7.7 we see that the loop has been defined and placed inside {} braces to indicate the constraint. Since there is no obvious place on a loop to hang this constraint, we just hang it on one of the associations in the loop, R2 in this case. It would be perfectly legal and mean the same thing if we had placed the constraint on R1 or R3 instead.
Nonetheless, we choose to put the constraint on R2 because, most likely, that's where potential links will need to validate themselves against the constraint before being created. Professors will be hired by Departments prior to taking on Students. Students may select majors prior to selecting advisors. Students with declared majors will then be restricted when they go looking for potential advisors. Of course, nothing in the class diagram prevents a Student from first selecting an advisor and then a major. If you think this is more likely to happen, then put the loop constraint on R3. The choice of where to hang the constraint is an issue of clarity. As long as you put it on one of the paths in the loop you're okay.
Enforcement of the constraint
The constraint notation on the loop is nothing more than that. Notation. In Executable UML, notation must have a deeper meaning to result in real code. In my project experience so far, we have expressed constraints in the procedure models in proprietary1 action language. The procedure that creates a link along R2 or R3 would have to ensure that only valid instances were linked.
In the near future, we anticipate the use of statements written in OCL (Object Constraint Language). These statements could then be processed to generate the required action language or target code.
Unconstrained loops are not necessarily bad
There is nothing wrong with an unconstrained loop as long as it accommodates the requirements precisely. If our university changes its rules so that a Student may choose any Professor as an advisor, regardless of major, then Model 7.6 becomes the correct model. The analyst must understand the application in enough detail to specify the correct level of constraint on all loops.
Now let's take a look at more examples of loop constraints. We'll examine a simple loop involving only one association and then look at a more complex pattern involving multiple loops.
Single loop
Imagine we model a scripting language where a Script consists of sequentially executing Steps.
Should the R4 loop be constrained? In the unconstrained case any instance of Step could be linked to execute before or after any other instance of Step. But that wouldn't make sense, since execution order is defined local to each Script. In other words, Steps are ordered within a single Script. We wouldn't want the "Power Laser OFF" step in the "Laser Diagnostic" script to be followed by the "Align Platform" step in the "Scan Wafer" script followed by the "Power Laser ON" step in the "Laser Diagnostic" script.
So the constraint on R4 is this: "An instance of Step may execute after or before another instance of Step in the same Script". Furthermore, an instance of Step must execute after a step other than itself.
The necessary constraints can be illustrated in a set diagram:
We can see from Figure 7.4 that links along R1 partition the set of Steps since every Step belongs to exactly one Script. When a link on R4 is created, it originates from one instance of Step and terminates on another instance of Step in the same R1-partitioned subset.
The notation in Model 7.9 below places the appropriate constraints on R4.
As a matter of style, when more than one constraint appears on an association, I proceed from the general to the specific. A link along R4 may connect to the subset defined by an existing link on R1 and then only to those instances in that subset that do not include the origin instance.