For being able to easily retrieve the committees that are chaired or co-chaired by a club member, we add two reference properties to our Committee-ClubMember example model: the property of a club member to be the chair of a committee (ClubMember
::chairedCommittee
) and the property of a club member to be the co-chair of a committee (ClubMember
::coChairedCommittee
). We assume that any club member may chair or co-chair at most one committee (where the disjunction is non-exclusive). So, we get the following model:
Notice that there is a close correspondence between the two reference properties Committee
::chair
and ClubMember
::chairedCommittee
. They are the inverse of each other: when the club member Tom is the chair of the budget committee, expressed by the tuple ("budget committee", "Tom"), then the budget committee is the committee chaired by the club member Tom, expressed by the inverse tuple ("Tom", "budget committee"). For expressing this inverse correspondence in the diagram, we append an inverse property constraint, inverse of chair
, in curly braces to the declaration of the property ClubMember
::chairedCommittee
, and a similar one to the property Committee
::chair
, as shown in the following diagram:
Using the reference path notation of OOP languages, with c referencing a Committee
object, we obtain the equation:
Or, the other way around, with m referencing a ClubMember
object, we obtain the equation:
Notice that when a property p2 is the inverse of a property p1, this implies that, the other way around, p1 is the inverse of p2. Therefore, when we declare the property ClubMember
::chairedCommittee
to be the inverse of Committee
::chair
, then, implicitly, Committee
::chair
is the inverse of ClubMember
::chairedCommittee
. We therefore call Committee
::chair
and ClubMember
::chairedCommittee
a pair of mutually inverse reference properties. Having such a pair in a model implies redundancy because each of the two involved reference properties can be derived from the other by inversion. This type of redundancy implies data storage overhead and update overhead, which is the price to pay for the bidirectional navigability that supports efficient object access in both directions.
For maintaining the duplicate information of a mutually inverse reference property pair, it is common to treat one of the two involved properties as the master, and the other one as the slave, and take this distinction into consideration in the code of the change methods (such as the property setters) of the affected model classes. We indicate the slave of an inverse reference property pair in a model diagram by declaring the slave property to be a derived property using the UML notation of a slash (/
) as a prefix of the property name as shown in the following diagram:
The property chairedCommittee
in ClubMember
is now derived (as indicated by its slash prefix). Its annotation {inverse of chair}
defines a derivation rule according to which it is derived by inverting the property Committee
::chair
.
In a UML class diagram, the derivation of a property can be specified by an OCL derive expression that evaluates to the value of the derived property for a given object. In the case of a property being the inverse of another property, specified by the constraint expression {inverse of
anotherProperty}
appended to the property declaration, the derivation expression is implied. In our example, for any club member object m
, it evaluates to the committee object c
such that c.chair = m
. In OCL, this derivation rule would be expressed like so
context ClubMember::chairedCommittee: Committee derive: if Committee.allInstancea().exists( c | c.chair = self) then c
There are two ways how to realize the derivation of a property: it may be derived on read via a read-time computation of its value, or it may be derived on update via an update-time computation performed whenever one of the variables in the derivation expression (typically, another property) changes its value. The latter case corresponds to a materialized view in a database. While a reference property that is derived on read may not guarantee efficient navigation, because the on-read computation may create unacceptable latencies, a reference property that is derived on update does provide efficient navigation.
When we designate an inverse reference property as derived by prefixing its name with a slash (/
), we indicate that it is derived on update. For instance, the property /chairedCommittee
in the example above is derived on update from the property chair
.
In the case of a derived reference property, we have to deal with life-cycle dependencies between the affected model classes requiring special change management mechanisms based on the functionality type of the represented association (either one-to-one, many-to-one or many-to-many).
In our example of the derived inverse reference property ClubMember
::chairedCommittee
, which is single-valued and optional, this means that
whenever a new committee object is created (with a mandatory chair
assignment), the corresponding ClubMember
::chairedCommittee
property has to be assigned accordingly;
whenever the chair
property is updated (that is, a new chair is assigned to a committee), the corresponding ClubMember
::chairedCommittee
property has to be unset for the club member who was the previous chair and set for the one being the new chair;
whenever a committee object is destroyed, the corresponding ClubMember
::chairedCommittee
property has to be unset.
In the case of a derived inverse reference property that is multi-valued while its inverse base property is single-valued (like Publisher
::publishedBooks
in Figure 7.4 below being derived from Book
::publisher
), the life cycle dependencies imply that
whenever a new 'base object' (such as a book) is created, the corresponding inverse property has to be updated by adding a reference to the new base object to its value set (like adding a reference to the new book object to Publisher
::publishedBooks
);
whenever the base property is updated (e.g., a new publisher is assigned to a book), the corresponding inverse property (in our example, Publisher
::publishedBooks
) has to be updated as well by removing the old object reference from its value set and adding the new one;
whenever a base object (such as a book) is destroyed, the corresponding inverse property has to be updated by removing the reference to the base object from its value set (like removing a reference to the book object to be destroyed from Publisher
::publishedBooks
).
Notice that from a purely computational point of view, we are free to choose either of the two mutually inverse reference properties (like Book
::authors
and Author
::authoredBooks
) to be the master. However, in many cases, associations represent asymmetrical ontological existence dependencies that dictate which of the two mutually inverse reference properties is the master. For instance, the authorship association between the classes Book
and Author
represents an ontological existence dependency of books on their authors. A book existentially depends on its author(s), while an author does not existentially depend on any of her books. Consequently, the corresponding object lifecycle dependency between Book
and Author
implies that their bidirectional association is maintained by maintaining Author
references in Book
::authors
as the natural choice of master property, while Author
::authoredBooks
is the slave property, which is derived from Book
::authors
.