Expanding Existing Languages
Something somewhere must tell the compiler that, for example, class Aggregate represents the association while classes ParentAggregate and ChildAggregate are a part of its implementation. Also, the compiler must know that when invoking Aggregate<Net,Pin>, an instance of AggregateParent must be inserted into Net and an instance of ParentChild into Pin. The best way to provide this information would be to add two new keywords; in Listing Four (available online), we used keywords Association and Participants.
When implementing the association, we also need to parameterize member names, which is something that existing templates/generics do not support. There are three ways to get around this last hurdle:
Style 1: Templates use a special parameter Name to parameterize member names, as in Listing Four.
Style 2: Implementation uses a new keyword, id, and the compiler replaces it by the association ID, for example:
template<class Parent,class Child> class Aggregate { Participants(ParentAggregate, ChildAggregate); void add(Parent *p, Child *c){ p->id.children.add(c); c->id.parent=p; }
Style 3: No additional keywords. The compiler is just smarter. In templates that implement the associations, it detects references to participating classes and transparently inserts ID:
template<class Parent,class Child> class Aggregate { Participants(ParentAggregate, ChildAggregate); void add(Parent *p, Child *c){ p->children.add(c); c->parent=p; // which is interpreted as // p->ID.children.add(c); // c->ID.parent=p; }
The implementation should let the debugger step through all the code when debugging association-based applications as well as when debugging associations themselves.
Interim Implementation
Until the time languages and compilers support reusable associations, we implemented a library of associations, which uses a code generator (precompiler). The implementation and the application interface are the same in both C++ and Java. (For free source-including documentation, see www.codefarms.com/incode.htm.) The precompiler is coded with the library and uses itself to recompile.
Because the code generator must substitute member names in the association files, we decided not to use templates and do all the substitutions with the precompiler. The result is a simple scheme that makes coding of new associations easy and readable (Listing Five; available online). This approach is a cross between style 1 and style 2. The library inserts each group of members as one instance of an automatically generated class (ZZ_Net, ZZ_Pin,...).
When interpreting the implementation of:
Association Aggregate<Net,Pin> pins;
the code generator performs the following substitutions:
$$ is replaced by pins $0 ZZds._pins $1 Net $2 Pin
Instead of using keyword Participants, the library registry file (Listing Five) describes the roles of participating classes and the UML representation of individual associations. For example, B1-* means "bidirectional one-to-many."
Conclusion
We have no doubt that eventually, libraries of associations will supersede existing class libraries. The new libraries will provide a uniform treatment for existing containers, intrusive data structures, associations, structural design patterns, and other data organizations missing in the standard libraries today. Associations will become first-class entitiesequally visible and important as classes. Experience with several libraries based on this ideaone of them used commercially for over 18 yearsshows that this approach is viable and significantly increases readability, reusability, and supportability of the resulting software.