Solution to dc38: Email management system
See code at solutions/code/tutorialquestions/questiondc38
The solution assuming that group relationships are acyclic is given in the acyclic
package. I have used an abstract class, EmailAddress
,
to hold an email address's identifier (common to individual and group addresses), and to implement toString()
and equals()
, which are purely
based on this identifier. Because I have overridden equals()
I have dutifully overridden hashCode()
too.
I have declared getTargets()
abstract in EmailAddress
:
public abstract Set<EmailAddress> getTargets();
Then I have made two subclasses, IndividualEmailAddress
and GroupEmail``Address
. IndividualEmailAddress
simply overrides getTargets()
by returning a set
containing the individual email address. GroupEmailAddress
keeps a set of email addresses, members
to store members of the group. The getTargets()
method is overridden
by recursively calling getTargets()
on each member, and constructing a set that is the union of all these results. (Note that the group email address is not itself added to the result of getTargets()
;
this method was required to include only individual email addresses.)
Think about the interplay between recursion and polymorphism in the implementation of getTargets()
in GroupEmailAddress
.
Package cyclic
shows how I have adapted the implementation of getTargets()
to handle cyclic relationships. I have done this by implementing getTargets()
in
the abstract class EmailAddress
, and making this implementation final
so that I do not accidentally override it elsewhere. This getTargets()
method calls
a helper method:
protected abstract Set<EmailAddress> getTargets(Set<EmailAddress> alreadySeen);
whose purpose is to find all targets recursively, but to stop recursing when an email address in the set alreadySeen
is encountered. This method
is abstract because it needs to be implemented differently in IndividualEmailAddress
and GroupEmailAddress
. It is protected so that
it cannot be accessed by classes outside the EmailAddress
inheritance hierarchy.
Unfortunately this is not true: because protected visibility is
more visible than package visibility this method can be seen by any classes in the cyclic
package. But it is the best we can do in Java!
The implementation of getTargets(alreadySeen)
in cyclic.IndividualEmail``Address
is identical to that for getTargets()
in
acyclic.IndividualEmailAddress
.
The implementation of getTargets(alreadySeen)
in cyclic.GroupEmailAddress
is similar to that for getTargets()
in acyclic.GroupEmailAddress
except that it adds the following lines at the start:
if(alreadySeen.contains(this)) {
return result;
}
alreadySeen.add(this);
These lines ensure that we do not get into an infinite recursive loop when the relationship between email groups is cyclic.