Relationships -- when I use the term relationship, I mean the Unified Modeling Language (UML) concepts of association, aggregration, and composition -- are implemented via the combination of attributes and methods (operations). The attributes describe the relationship, and the methods define and update the relationship. In An overview of relationships, I discussed the fundamentals of object relationships. We have described implementation strategies in both Implementing object relationships with a singular multiplicity and Implementing one-to-many object relationships. Now I would like to describe how to implement a bidirectional many-to-many relationship.
A many-to-many relationship is one that has multiplicities of many -- one
of 0..*, 1..*, 0..n, or 1..n where n is a number > 1 -- at both ends of the
relationship. For example, in Figure 1 you see that there is a
many-to-many relationship, "instructs", between the Professor and Seminar
classes. There is also a one-to-many association, "oversees", between these
two classes.
Figure 1: The Professor and Seminar classes

Many-to-many relationships require the most amount of work to implement
because each object involved in the relationship must maintain a
collection of references to the other objects to which it is related. You
saw in Implementing one-to-many object
relationships how to implement the many sides of an association via
the use of a collection class (in that case I used a HashSet), a getter
and setter for the collection, and operations to insert and remove items
from the collection. When using a many-to-many association you merely need
to do this in both classes.
Listing 1 presents the scaffolding code in the Professor class to manage
its part of the "instructs" association with instances of Seminar. Listing
2 presents the similar code for the Seminar class to manage its part of
the association. I didn't include the header documentation or the source
code for the accessors for the sake of brevity.
Notice how, in Listing 1, the addSeminar(seminar) method automatically
invokes addInstructor(professor) in the Seminar class and, in Listing 2,
this method does the same thing in the other direction. This code is
robust because it automates the management of the association in both
directions, reducing the chance of introducing a logic error in your code.
Listing 1. Managing the association from a Professor to the Seminars he/she instructs
private HashSet seminars;
public void addSeminar(Seminar seminar)
{
// If the seminar is not already in the collection add it
// The if statement avoids an infinite loop managing the association
if ( ! getSeminars().contains(seminar)) {
getSeminars().add(seminar);
// The Seminar should know who instructs it
seminar.addInstructor(this);
}
}
public void removeSeminar(Seminar seminar)
{
// Only perform the removal of the seminar if it is in the collection
// The if statement avoids an infinite loop managing the association
if ( getSeminars().contains(seminar)) {
// Remove the seminar from the collection
getSeminars().remove(seminar);
// Update the seminar so that it knows that the professor no longer instructs it
seminar.removeInstructor(this);
}
}
|
Listing 2. Managing the association from a Seminar to the Professors who teach it
private Vector instructors;
public void addInstructor(Professor professor)
{
// If the professor does not exist in the collection add it
// The if statement avoids an infinite loop managing the association
if ( ! instructors.contains( professor )) {
getInstructors().add( professor );
// Update the other end of the association
professor.addSeminar( this );
}
}
public void removeInstructor(Professor professor)
{
if ( instructors.contains( professor )) {
getInstructors().remove( professor );
// Update the other end of the association
professor.removeSeminar( this );
}
}
|
Adhering to coding conventions
Also notice the use of whitespace in the parameter list of method invocations in Listing 2, as compared to Listing 1. Both styles are fine, just pick one and follow it consistently in your code. If you're interested in Java coding style conventions, I highly recommend the book The Elements of Java Style (see Resources).
-
The Object Primer 2nd Edition
by Ambler, S.W. New York: Cambridge University Press, 2001.
-
The Unified Modeling Language Reference Manual
by James Rumbaugh, Grady Booch, and Ivar Jacobson. Reading, MA: Addison-Wesley Longman, Inc., 1999.
-
Understanding Relationships - Object Modeling Tools Guide
from the
Persistence software site.
-
An Integrated and Enhanced
Methodology for Modeling and Implementing Object Relationships
by Byron K. Ehlmann and Gregory Riccardi.
-
Building Object Applications That Work
by Ambler, S.W. New York: Cambridge University Press, 1998.
-
The Elements of Java Style
by Vermeulen, A., Ambler, S.W., Bumgardner, G., Metz, E., Misfeldt, T., Shur, J., & Thompson, P. New York: Cambridge University Press, 2000.
Scott W. Ambler is President of Ronin International, a consulting firm specializing in object-oriented software process mentoring, architectural modeling, and Enterprise JavaBeans (EJB) development. He has authored or co-authored several books about object-oriented development, including the recently released The Object Primer 2nd Edition, which covers, in detail, the subjects summarized in this article. He can be reached at scott.ambler@ronin-intl.com and at his Web site at www.ambysoft.com.




