In the left diagram, the cardinalities allow
- 1 object of type A
- 1 object of type B
- 1 object of type A + 1 object of type B
- no objects
Cardinalities affect only the multiplicity of the association end where they apply. You need to write the last part of the condition (at least one A or at least one B) as a constraint (invariant). An invariant is a condition that always holds for every instance of that class.
If you write the constraint as pseudocode, it is enough to say that a!=null or b!=null
.
You draw the invariant as a note with stereotype «invariant» attached to the constrained class.
In OCL (a language used for non-ambiguous description of constraints):
inv: not a.oclIsUndefined() or not b.oclIsUndefined()
(You can read more about OCL here)
The right diagram introduce a super-class Object
that is not part of the description of your problem. There could be another classes that specialize Object
(e.g. C
) that you don't want in your association. In this case the invariant is more complex, because you need to require that:
- There is at most one instance of A
- There is at most one instance of B
- There are no instance of other classes.
(If you write the invariant in natural language, it is enough to mention the items above).
In OCL, assuming that the association end is named x
, the invariant reads:
inv: x->select(oclIsKindOf(A))->size()<=1 and
x->select(oclIsKindOf(B))->size()<=1 and
x->select(not oclIsKindOf(A) and not oclIsKindOf(B))->isEmpty()
or
inv: let a:Integer = x->select(oclIsKindOf(A))->size() in
let b:Integer = x->select(oclIsKindOf(B))->size() in
a<=1 and b<=1 and x->size()=a+b