Table of Contents
A bidirectional association is an association that is represented as a pair of mutually inverse reference properties.
The model shown in Figure 14.1 below (about publishers, books and their authors) serves as our running example in all other parts of the tutorial. Notice that it contains two bidirectional associations, as indicated by the ownership dots at both association ends.
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 chair a committee
(ClubMember
::chairedCommittee
) and the property of a club member
to co-chair 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:
We also call Committee
::chair
and
ClubMember
::chairedCommittee
a pair of mutually inverse reference properties. Having such
a pair in a model means having 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 good practice 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
now has a slash-prefix
(/
) indicating that it is derived. The {inverse of chair}
annotation
in this example defines the derivation rule: the property is derived by inversion of
Committee
::chair
. This implies that, the other way around,
Committee
::chair
is the inverse of
ClubMember
::chairedCommittee
.
In a UML class diagram, the derivation of a property can be specified, for instance, by an
Object Constraint Language (OCL) expression that
evaluates to the value of the derived property for the 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, it evaluates to the
committee object reference c
such that c.chair = this
.
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 an SQL 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.
In the case of a derived reference property, the derivation expresses life cycle dependencies. These dependencies require special consideration in the code of the affected model classes by providing a number of 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 updated as
well;
whenever a committee object is destroyed, the corresponding
ClubMember
::chairedCommittee
property has to be
unassigned.
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 14.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
).
We use the slash-prefix (/
) notation in the example above for indicating that
the property ClubMember
::chairedCommittee
is derived on update from
the corresponding committee object.