The three class approach is tempting because it mirrors correct database design, mapping one class to one table. It's very elegant from a high-level conceptual design, but is usually overly complex in an actual implementation, and therefore rarely used in popular APIs. Usually relationships are described by both of the involved objects, as in facebook's event and user APIs.
For example, look at GUI toolkits with containers full of widgets. You call container.add(widget)
, which internally calls something like widget.setContainer(container)
. Once the container-widget relationship is established, sometimes you call container.getWidgets()
, and sometimes you call widget.getContainer()
.
Now consider how this would be implemented with a third, ContainerWidget
object. Every time you wanted to create an association, it would look like this:
containerWidget = new ContainerWidget(container, widget);
container.addContainerWidget(containerWidget);
widget.setContainerWidget(containerWidget);
To get a widget's container would look like this:
widget.getContainerWidget().getContainer();
Eventually someone's going to get annoyed with typing that, and just create a widget.getContainer()
.
Getting all the widgets for a container is even more complicated:
containerWidgets = container.getContainerWidgets();
for (containerWidget in containerWidgets)
widgets.append(containerWidget.getWidget());
Eventually someone's going to get annoyed with that, and create a container.getWidgets()
. Then eventually someone will notice that since we already have those methods anyway, and rather than rebuild the list every time it's called, it would be a lot easier just to store a list of widgets in the container and a pointer to the container in the widget.
See where I'm going with this? Either you end up with unnecessary complexity, or you eventually simplify down to the two class solution.
The exception to the rule is when you need to describe attributes of the relationship itself. For example, a spousal relationship between two people doesn't merit a third class. You just put a Person
pointer in the Person
class to point to the current spouse. However, a marriage relationship merits a third class, because marriages have places, start dates, end dates, etc. that describe aspects of the relationship other than the mere fact it exists.