Browse DevX
Sign up for e-mail newsletters from DevX


Wed Yourself to UML with the Power of Associations, Part 2 : Page 2

By thinking of associations as marriages, we've hit on a way of making even complex associations easy to learn. In part 2 of this series on UML associations, we look at a way of upgrading associations to classes, in order to use instantiation.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Implementing Association Classes
Now that you know what an association class means and how its instances are characterized, we can have a look at how they should be implemented. Central in the implementation is that you want to use the association that has an association class attached, in the same way as a normal association. That is, in the one-to-many, or many-to-many situation you want to be able to add a husband to a Woman instance by calling the operation addToHusbands, whereas in the one-to-one situation you would like to use the operation setHusband. Adding a husband to a Woman instance implies the creation of an instance of the association class Marriage. Our implementation ensures that these instances are created only when one of the addToHusbands or setHusband operations is called. Creating an instance of an association class in a different manner would cause inconsistencies in the system.

In the previous article we differentiated between four configurations of association ends: one-to-one, one-to-many, many-to-many, and the one-way navigable association. The first three configurations also appear in combination with an association class. One-way navigable associations are not useful when an association class is attached, therefore we will not discuss this configuration.

The Association Class Itself
The implementation of the association class itself is the same for all three configurations. The implementation should hold two fields that are pointers to the two objects that are related through the association class instance. The type of the one field is the class at the one end and the type of the other field is the class at the other end. Each field should have a normal get operation, but there should not be a set operation. The fields get their value on creation of the association class instance, as shown in the following code.

public class Marriage { ... private Man f_husband = null; private Woman f_wife = null; public Marriage(Man a, Woman b) { if ( a != null && b != null ) { // can't relate an object to null this.f_husband = a; a.z_internalAddToMarriage(this); this.f_wife = b; b.z_internalAddToMarriage(this); } } public Man getMyHusband() { return f_myHusband; } public Woman getMyWife() { return f_myWife; } ... }

The constructor of the Marriage class takes cares of setting up the link between the two partners by calling the special z_internalAddToMarriage operations of both. This operation should only be called from the constructor of the Marriage class, which explains its obscure name. Internally the link between the two partners is implemented as a link from the Woman object to the Marriage object, which in turn holds a link to the Man object, and vice versa. In Figure 3, which shows the implementation, the arrows represent the pointers between the instances.

Figure 3. A Pointed Marriage: An implementation of the one-to-one association class is shown.
The Partner Classes in the One-To-One Situation
Both classes at the ends of the association can be implemented in almost the same way in the one-to-one situation. Each class should have a field, whose type should be the association class. There should be a get operation for this field (getMarriage), but no set operation. Instead the setMyWife and setMyHusband operations should be used. These operations are the most important ones, because they are responsible for making the new partner object aware of the link. Let's have a look at the code for the Man class.

public class Man { ... private Marriage f_marriage = null; public void setMyWife(Woman element) { if ( this.f_marriage != null ) { // this man is already married, remove the old marriage ((Marriage)this.f_marriage).clean(); } if ( element != null ) { if ( element.getMarriage() != null ) { // the new wife is already married, remove the old marriage ((Marriage)element.getMarriage()).clean(); } // create the new marriage this.f_marriage = new Marriage(this, element); } else { // cannot marry a null object, so the marriage is set to null this.f_marriage = null; } } ... }

As in the case of the normal association, you first check whether this Man object is already married. If so, you delete the old marriage in favor of the new one. This is done by calling the clean operation of the Marriage class:

public void clean() { f_myHusband.z_internalRemoveFromMarriage(this); f_myHusband = null; f_myWife.z_internalRemoveFromMarriage(this); f_myWife = null; }

In effect this operation removes all of the arrows from Figure 3 that showed the implementation. The z_internalRemoveFromMarriage operations set the pointer to the marriage instance to null. You rely on the garbage collector to actually remove the Marriage instance. Note that this implementation upholds the ABACUS rules; when the relationship is dissolved, the association class instance is dissolved as well. If the association class instance is referenced by other objects, these references should be cleared as well, otherwise the garbage collector will not remove the marriage instance. This situation is not handled in the code shown. However, removing an instance while there are still references to it is a bad idea anyway, therefore you should take care that these references are explicitly cleaned up before deleting the association.

Next, check whether the new wife object is already married. If so, her old marriage is deleted. Finally, you create a new Marriage instance. This is the only line that is different for the setMyHusband and setMyWife operations. The Marriage constructor takes as parameters first a Man and second a Woman instance, therefore you need to switch the order of the actual parameters. The call to the constructor in setMyHusband is:

this.f_marriage = new Marriage(element, this);

Any Man instance must be able to use the role name to refer to the Woman instance it is related to. Because the implementation does not provide a direct pointer to the Woman instance, we have to implement the getMyWife operation using the reference provided by the Marriage instance:

public Woman getMyWife() { if ( this.f_marriage != null ) { return this.f_marriage.getMyWife(); } else { return null; } }

Thanks for your registration, follow us on our social networks to keep up-to-date