GENERATING FLEXIBLE CODE FOR ASSOCIATIONS
Mayer Goldberg and Guy Wiener
Department of Computer Sciences, Ben-Gurion University, 1 Ben-Gurion Blvd, Be’er-Sheva, Israel
Keywords:
Associations, Code generation, Model-driven Architecture, Meta-programming.
Abstract:
Generating code for associations is one of the most fundamental requirements from a model-based code gen-
erator. There are several approaches for implementing associations, ranging from using basic collections
frameworks to using a database. The choice between them derive from the current requirements of the soft-
ware: Whether parallelism, caching or persistency required for a relation. Hard-coding a specific design choice
makes it difficult to alter it later. In this work, we propose a scheme that allows for automatic code generations
of associations with different features, without requiring manual changes to the code. These features include
using indices, traversing the association in parallel, or using an external database. Instead of the sequential
iterator interface, we propose to use an interface that is based on operations over collections, such as Foreach,
Filter, Map and Fold. This interface allows for writing operations that traverse the association without being
dependent on the implementation details of the generated code.
1 INTRODUCTION
Generating code from UML class diagrams requires
translating the high-level UML specifications into
concrete statements in some programming language.
UML specifications differ from statements in Object-
Oriented Programming Languages (OOPL) in the fol-
lowing aspects:
1. UML statements have a richer semantics then
their OOPL counterparts: Not every element in a
UML diagram can be expressed as a single state-
ment in an OOPL.
2. UML is more abstract then OOP code: It specifies
the expected structure and behavior of software
components, not how to implement it.
The gap between the semantics of UML associ-
ations and OOPL has been discussed extensively in
the UML literature. The full semantics of UML as-
sociations, as described in UML reference manual
(Rumbaugh et al., 2004), includes bi-directionality
and multiplicity constraints, as well as more advanced
features that have no direct equivalence in OOPLs:
Relations between associations (such as subset or re-
definition), association classes, and general OCL con-
straints
1
. In their seminal work on Object-Oriented
1
For details on OCL, see (OMG, 2005; Warmer and
Kleppe, 2003)
Analysis and Design (OOAD), Martin and Odell dis-
cuss this problem (Martin and Odell, 1992). They
suggest to implement bi-directional associations by
using either pairs of references or association ob-
jects. Craig Larman suggests refining bi-directional
associations to uni-directional ones (Larman, 2004).
Several works (Fowler, 2003; Gessenharter, 2008;
Akehurst et al., 2007) offer different implementa-
tions to associations that preserve more of their orig-
inal semantics. It is clear from these works that im-
plementing associations requires several code state-
ments. This fact, as well as the effort involved in
changing the implementation, suggests that code gen-
eration would be useful.
The second item, namely that UML is more ab-
stract then OOP code, is commonly overlooked in the
literature. The gap between the specification and the
code is intentional. Harrison et al., in their detailed
work on mapping UML specifications to Java, ex-
plain that a model expressed independently of a spe-
cific implementation provides greater flexibility (Har-
rison et al., 2000). This flexibility is required: Ac-
cording to (Pigoski, 1996), the cost of maintenance is
90% from its development cost, and (Martin and Mc-
Clure, 1983) show that 80% of the maintenance cost
are dedicated to perfective activities
2
and adaptive ac-
2
Activities that improve the software
96
Goldberg M. and Wiener G. (2010).
GENERATING FLEXIBLE CODE FOR ASSOCIATIONS.
In Proceedings of the Fifth International Conference on Evaluation of Novel Approaches to Software Engineering, pages 96-104
DOI: 10.5220/0002999700960104
Copyright
c
SciTePress
tivities
3
. Therefore, any re-use of the model that is
independent of a platform or performance constraints
can lower the cost of maintenance. Many UML state-
ments can be implemented in more than one way.
The semantics of UML associations only specifies
the relations that the system should maintain. The de-
tails of how to access, traverse, and modify the re-
lation are left for the implementer. All of the works
mentioned above focus on providing a single imple-
mentation to associations, based on standard collec-
tions frameworks For example, see the Java collec-
tions tutorial (Bloch, 1995). This approach provides
the richer semantics of associations in code, but does
not allow for replacing the implementation without
re-writing the code. For example, most standard col-
lections frameworks provide only sequential traver-
sal over an entire collection. This limitation neither
appears nor is implied by the UML standard
4
. We
would like to allow the developer to choose between
sequential and parallel traversal by changing a prop-
erty of the association, without re-writing the code.
Fast access to elements in a collection with specific
values requires in most frameworks adding a support-
ing data structure explicitly (E.g., a hash-table that
maps from integers to persons in a collection that are
of a given age). We would like to allow the developers
to add such indices as a property of the association, or
even let a smart code-generator decides which indices
should be used.
In this work we present flexible code generation
scheme that allows for replacing the generated imple-
mentation of associations without requiring changes
to the rest of the code. Section 2 describes the in-
terfaces for accessing, updating, and traversing over
associations. Section 3 describes different schemes
for generating implementations for those interfaces.
Section 4 provides an example of implementing and
using this scheme to improve the implementation of
associated classes. Section 5 concludes.
2 INTERFACES
To replace the implementation of associations in-
dependently of the client code
5
, we must provide
3
Activities that adapt the software to new platforms
4
Class diagram specifications do not discuss any oper-
ations over associations. The relevant sequence diagrams
specifications, such as for loops and multiple instances, also
omit these details. See the UML reference manual (Rum-
baugh et al., 2004).
5
“Client code” here refers to the code that uses the asso-
ciations, as opposed to the code that implements the associ-
ations
uniform interfaces for operations over associations.
These operations include:
1. Adding and removing pairs of associated in-
stances from the relation.
2. Traversing over the instances associated with a
given object.
We ignore the case of changing the set of instances
that are associated with a given object, since it can
be implemented by adding and removing pairs from
the relation. Operating on pairs instead of unrelated
collections allows the implementation to enforce bi-
directionality and multiplicity constraints. This ap-
proach is common to many of the works mentioned
above.
The second kind of operations, traversing associ-
ated instances, requires special attention. Most work
on associations propose to use a variation of the Iter-
ator pattern (Gamma et al., 1995). This pattern pro-
vides a way to perform a sequential traversal over a
data structure without exposing its implementation.
This approach, however, has several drawbacks:
1. It forces the traversal to be sequential and does
not enable parallel traversal, even when there are
no data dependencies between the iterations.
2. It does not encapsulate the selection of elements.
Therefore, optimizations like using hash-tables or
caching must be a part of the client code.
3. Iterators, together with loop commands (for,
while, etc.) often serve for implementing oper-
ations over a range of elements. The body of the
loop represents an operation over a single element
and is dependent on its internal structure. The iter-
ator pattern encapsulates the traversal, but exposes
the structure of the traversed elements. Moving
this implicit operation to a method in the class of
the element and explicitly applying this method
on all associated instances would provide better
encapsulation.
To overcome these drawbacks, we propose to use
different set of interfaces, similar to the one outlined
in (Martin and Odell, 1992):
The Association interface represents the relation
between two types.
The AssocEnd interface represents the collection
of instances that are associated with a given ob-
ject.
The Foreach, Map, Filter, and Fold interfaces rep-
resent operations over a collection of instances.
They are called aggregator interfaces.
The aggregator interfaces are generated and in-
clude sub-sets of the messages from the associated
GENERATING FLEXIBLE CODE FOR ASSOCIATIONS
97
type. The implementations of these interfaces are also
generated and include the concrete operations over
the collection. The Association and AssocEnd inter-
faces are not generated, since that their operations sig-
nature are constant. However, the implementations of
AssocEnd are parametrized by the concrete type of
aggregators that they returns. Therefore the imple-
mentations of AssocEnd are either generated, or take
AssocEnd factories as parameters (See the factory de-
sign pattern at (Gamma et al., 1995)). Similarly, the
implementation of Association is also parametrized
by the concrete type of AssocEnds that it returns.
Figure 1 shows these interfaces. Figures 2 and 3
show how these interfaces would overcome the prob-
lems described above. Figure 2 is an example of loop
that breaks the encapsulation of Person. Figure 3
shows our approach: The access to the properties of
Person is moved to methods and the loop is replaced
with using the above interfaces. The following sec-
tions describe these interfaces in greater detail.
Figure 1: The interfaces for associations.
for ( Person p: getEm p l o ye e s () ) {
if (p . g e N a m e (). getSa l a r y () > limit ) {
p . set S a l a ry ( p . get S a l a r y ( ) *0.9) ;
}
}
Figure 2: An example of the body of the loop that breaks
encapsulation.
2.1 The Association Interface
The AssociationhA,Bi interface represents the relation
itself. It contains the operations for adding and re-
moving pair from the association. The get operations
return an AssocEnd. The getter methods of the partic-
ipating classes, A and B, delegate to the get operations
public class P e r s on {
...
public boolean ea r n s Mo r e Th a n (int x ) {
return g e tSa l a r y () > x ;
}
public void giv e R a i s e (double d ) {
set S a l a ry ( g e t S a l ary () * d );
}
}
ge t E m pl o y e es ()
. f i l t e r ( ). e a r ns M o re T h a n ( limit )
. f o r e a c h (). give R a i s e (0.9);
Figure 3: An encapsulation-preserving approach to associ-
ations.
in the association. Figure 4 shows a simple associa-
tion. Figure. 5 shows the Association interface and its
relations with the participating classes.
As mentioned above, the implementation of Asso-
ciation requires a decision which concrete AssocEnds
to return. This decision can be taken either at design
time, by hard-coding it or generating code for it, or at
run-time, by using a factory.
BA
m1
a
m2
b
f
Figure 4: A minimalistic example of an association.
Figure 5: The association interface and participating
classes.
2.2 The Association End Interface
The AssocEndhXi interface represent one end of the
association. The get operation in Association returns
an instance of this interface. This instance represents
all the instances of the opposite type that are asso-
ciated with the given object. The interface has two
kinds of operations:
The operation all returns the associated objects as
a platform-specific collection. For example, in our
Python implementation from Section 4, it returns
a list of all associated objects.
The operations foreach, map, filter, and fold return
an aggregator. The aggregator represents opera-
tions over the collection of associated instances,
without exposing the implementation details. The
aggregation interfaces are described below.
As mentioned above, the implementation of As-
socEnd requires a decision which concrete instances
ENASE 2010 - International Conference on Evaluation of Novel Approaches to Software Engineering
98
of foreach, map, filter and fold to return. These de-
cisions can be taken either at design time, by hard-
coding it or generating code for it, or at run-time, by
using a factory per aggregator type.
2.3 Aggregation Interfaces
Foreach, Map, Filter, and Fold are aggregation inter-
faces. They are named after higher-order functions
from functional programming languages. For exam-
ple, see the Haskell standard list library
6
. They rep-
resent operations over a set of associated instances.
The operations in each aggregation interfaces derive
from the type of the instances that it wraps. There-
fore, these interfaces are generated for each type that
participates in an association. Figure 6 shows an ex-
ample of a type and the aggregation interfaces that are
derived from it. The details for each interface follow.
Figure 6: The type Person and generated aggregation inter-
faces.
2.3.1 Foreach
The Foreach aggregator represents sending a message
m
i
to all the associated instances, where m
i
returns no
value. The implementation is not required to wait for
the return value, so it can be parallel or asynchronous.
Figure 7 shows a sequential generated code of a single
method in an Foreach aggregator. Applying a mes-
sage m on the associated instances of a is coded as
a.getB().foreach().m().
2.3.2 Map
The Map aggregator is similar to Foreach, but for
messages that has a return value. The map higher-
order function is described in Anatomy of LISP”
6
www.haskell.org/ghc/docs/latest/html/libraries/haskell98-
1.0.1.1/List.html
public class Fo r e a ch P e rs o n
implements Foreac h < Per son >
{
public void giv e R a i s e (double d ) {
for ( Person p: asso c i at e d Pe r s on s ) {
p . giv e R a i se ( d ) ;
}
}
}
Figure 7: A sequential implementation of the method
giveRaise in ForeachhPersoni.
(Allen, 1978), and was even a part of the APL pro-
gramming language (Iverson, 1962). It is similar to
the OCL collection operation collect, see (OMG,
2005) for details. The collection of return values is
returned as an aggregator interface itself. Again, the
decision which concrete AssocEnd to use can be taken
at design-time or run-time. Figure 8 shows a sequen-
tial generated code of a single method in a Map ag-
gregator. Since that Map requires the return values,
it can run in parallel or send asynchronous messages,
but must wait for all the responses to arrive. Getting
the mapped values of a message m on the associated
instances of a is coded as a.getB().map().m(). Ag-
gregated operations can be concatenated. For exam-
ple, a.getB().map().m().foreach().f().
public class Map P e r s o n
implements Map < Pers on >
{
public AssocEnd < Car > getCar () {
Assoc E n d < Car > ret =
(select a concrete AssocEnd);
for ( Person p: asso c i at e d Pe r s on s ) {
ret . a dd ( p . g e t C a r ( ) ) ;
}
return ret ;
}
}
Figure 8: A sequential implementation of the method get-
Car in MaphPersoni.
2.3.3 Filter
The Filter aggregator filters associated instances that
return true in response to a boolean method m
i
. It
is similar to the OCL collection operation select,
see (OMG, 2005) for details. Similarly to Map,
the selected instances are returned as an instance of
AssocEnd, whose concrete type is chosen either at
design- or run-time. The parametric type of the re-
turned association end is the same as the one of the
Filter aggregator. Figure 9 shows a sequential gen-
erated code of a single method in a Filter aggrega-
tor. Like Map, Filter can have a parallel or asyn-
GENERATING FLEXIBLE CODE FOR ASSOCIATIONS
99
chronous implementation, but it returns only after re-
ceiving all the responses. Getting the selected associ-
ated instances of a by a boolean message m is coded
as a.getB().filter().m().
public class Fi l t e rP e r s on
implements Filter < Perso n >
{
public AssocEnd < Pers on >
ea r n s Mo r e T h a n (double d )
{
Assoc E n d < Per son > r et =
(select a concrete AssocEnd);
for ( Person p: asso c i at e d Pe r s on s ) {
if ( p . ea r n s Mo r e T ha n ( d ) {
ret . a dd ( p . g e t C a r ( ) ) ;
}
}
return ret ;
}
}
Figure 9: A sequential implementation of the method earns-
MoreThan in FilterhPersoni.
2.3.4 Fold
The Fold aggregator performs an operation over the
associated instances where each step depends on the
result of the previous one.
7
It is similar to the OCL
collection operation iterate, see (OMG, 2005) for
details. The signature of a method that performs such
operation is such that it takes as a parameter a value
of the same type as it returns: m
i
(r : R) : R. The folded
operation returns the result of the last operation in this
chain. The implementation of Fold must be sequen-
tial. Figure 10 shows an example of a generated code
of a single method in a Fold aggregator. Getting the
final folded result r2 of message m over the associated
instances of a with initial value r1 is coded as R r2 =
a.getB().fold().m(r1).
public class Fo l d P e rso n
implements Fold < Per son >
{
public double su m S a lar i e s (double d ) {
double ret = d ;
for ( Person p: asso c i at e d Pe r s on s ) {
ret = p . su m S a la r i e s ( r et );
}
return ret ;
}
}
Figure 10: A sequential implementation of the method sum-
Salaries in FoldhPersoni.
7
The fold operation here is from the first elements to the
last. The opposite fold can be implemented in the same way.
3 IMPLEMENTATION
The Association, AssocEnd, and the aggregator inter-
faces only specify what are the operations over the
association, and not how to implement them. The
concrete implementation may vary in the following
aspects:
1. Where to store the relation itself.
2. How to implement the traversal operations of the
Foreach and Map aggregators: Sequentially or in
parallel.
3. How to implement the selection operations of the
Filter aggregator: By traversing the association or
using auxiliary data structures.
3.1 Storing the Relation
There are two options for storing the data of the rela-
tion:
Internal. The relation is stored in the main memory.
External. The relation is handled by an external
component, such as a database or a storage ser-
vice.
3.1.1 Internal Storage
Storing the relation in main memory, as a part of an
OOPL class hierarchy, has been discussed thoroughly
in the works mentioned above. Figure 11 shows a de-
sign example for implementing an association using
classes (in an OOPL). Figure 12 outlines the code for
this implementation.
Figure 11: Storing a relation in main memory.
3.1.2 External Storage
The interfaces discussed in Section 2 can act as a
uniform fac¸ade for persistency components, such as
databases or files. As is, the proposed interfaces can
not bridge the gap between the object-oriented design
and relational databases or sequential files. However,
it can provide a common interface for external persis-
tency solutions. For example:
ENASE 2010 - International Conference on Evaluation of Novel Approaches to Software Engineering
100
public class F
implements A s s o c i a t i o n <A , B >
{
public void add ( A a , B b ) {
a . b . add ( b );
b . a . add ( a );
}
public void r e m o ve ( A a , B b ) {
a . b . r e m o v e ( b );
b . a . r e m o v e ( a );
}
public A s s o c iationE n d <B > g et ( A a ) {
// return an AssociationEnd of a.b
}
public A s s o c iationE n d <A > g et ( B b ) {
// return an AssociationEnd of b.a
}
}
Figure 12: Implementing an association using the fields ap-
proach.
Object-to-Relational Mapping (ORM) systems,
e.g. Hibernate
8
Object-Oriented Databases (OODB), e.g. DB4O
9
Files See (Blaha and Premerlani, 1997) for
Object-to-File serialization techniques
Having a common interface to different solutions al-
lows the developer to switch between solutions seam-
lessly, thus removing a potential vendor lock. Fig-
ure 13 shows an example of hiding the persistency
details by using the Association interface: It includes
extra code to update a DB4O database.
public class F
implements A s s o c i a t i o n <A , B >
{
public void add ( A a , B b ) {
a . b . add ( b );
b . a . add ( a );
db . set ( a );
db . set ( b );
db . c o m m i t ( );
}
public void r e m o ve ( A a , B b ) {
a . b . r e m o v e ( b );
b . a . r e m o v e ( a );
db . set ( a );
db . set ( b );
db . c o m m i t ( );
}
}
Figure 13: Implementing an association using DB4O.
Marked lines are DB4O-specific.
8
http://www.hibernate.org
9
http://www.db4o.com
3.2 Traversal Operations
The iteration over an association does not have to be
sequential, as in the examples in Section 2.3. Unlike
Fold operations, Foreach, Map, and Filter operations
do not imply a specific order in which the aggregated
message is sent to the associated instances. Foreach
operations can be sent asynchronously, without wait-
ing for a reply. Figure 14 shows a sequence diagram
of performing an Foreach operation asynchronously.
Map and Filter operations must wait for returned val-
ues, but can still be performed in parallel. Figure 15
shows a sequence diagram of performing a Map oper-
ation using several threads.
The parallel implementation of traversal opera-
tions in similar to the implementation of parallel iter-
ators, as described in (Giacaman and Sinnen, 2008a;
Giacaman and Sinnen, 2008b). However, parallel it-
erators and parallel operations differ in several points.
First, parallel iterators require changes to the client
code. They implements the standard iterator inter-
face, but the programmer still has to add threads to
the code. Second, in the cases of map, filter and fold,
the programmer has to add a reduce method to col-
lect the results. Finally, parallel iterators encourage
exposing the structure of the traversed elements, just
as sequential ones (see Section 2).
Figure 14: A sequence diagram of an asynchronous Foreach
operation op(). Asynchronous operations are marked with a
non-filled arrow-head.
Figure 15: A sequence diagram of a buffered Map opera-
tion op() using K threads over N instances. Asynchronous
operations are marked with a non-filled arrow-head.
GENERATING FLEXIBLE CODE FOR ASSOCIATIONS
101
3.3 Selecting Instances
A common scenario of traversing over associated in-
stances is finding an instance, or a set of instances,
that satisfies a condition E.g., has a given name.
The operations in the Filter interface (Section 2.3.3)
represent this scenario. Figure 9 shows the most na
¨
ıve
implementation of this scenario.
A common optimization of this scenario is to keep
a cache E.g., a hash-table mapping names to in-
stances with that name. This optimization reduces the
run-time in an order of magnitude. Another possible
form of cache is to query a database, as discussed in
Section 3.1.2.
Implementing this optimization requires adding
code to the add and remove operation and change spe-
cific Filter operations. Therefore implementing it as
a part of the client code is cumbersome and makes
the client code dependent of the specific association
implementation. A better solution is to generate this
code as a part of the implementations of the Associ-
ation and Filter interfaces. By doing so, it de-couples
the client code from the optimization code. Figures 16
and 17 shows the modifications to Association and Fil-
ter when caching the property B.key. Figure 18 out-
lines a similar method that uses DB4O as an external
storage and cache.
public class F
implements A s s o c i a t i o n <A , B >
{
public void add ( A a , B b ) {
a . b . add ( b );
b . a . add ( a );
a . c a c h e . put ( b . key , b );
}
public void r e m o ve ( A a , B b ) {
a . b . r e m o v e ( b );
b . a . r e m o v e ( a );
a . c a c h e . r e m o v e ( b . key , b );
}
}
Figure 16: Caching the property B.key.
public class FilterB
implements Filt er < B >
{
public AssocEnd <B > is K e y ( Key k ) {
Assoc E n d < B > re t =
(select a concrete AssocEnd);
ret . a dd ( a . c ache . g et ( key ));
return ret ;
}
}
Figure 17: Selecting an instance of B by B.key using the
cache.
public class FilterB
implements Filter < B >
{
public AssocEnd <B > ke y e d ( Key k ) {
Assoc E n d < B > re t =
(select a concrete AssocEnd);
Query q = o b j e c t C on t a in e r . q u e r y ();
q . c o n s t r ain ( Class . B );
q . desc e n d ( " key " ). c o n s tra i n ( key );
ret . add ( q . execute ( ) );
return ret ;
}
}
Figure 18: Selecting an instance of B by B.key using DB4O.
Marked lines are DB4O-specific.
4 EXAMPLE
To demonstrate our scheme and its usage, we provide
the Python module assoc and code examples. The
module includes functions that generates code for as-
sociations, using the classes described above.
We decided to use Python in order to avoid the
need to generate source code as text. The assoc mod-
ule uses the ability of Python to generate at run-time
and return functions and classes as return values. It
deals directly with classes, rather then parsers and
templates.
4.1 Implementation Details
The assoc module works as follows:
1. The class Assoc n n is constructed with a descrip-
tion of the association and a set of factory func-
tions. The description includes the participating
classes and the names of the association ends.
The factory functions create the AssocEnd ob-
jects. Each such factory function takes additional
factory functions as arguments. The functions re-
turn the aggregators: Foreach, Map, Filter and
Fold.
2. When created, the association class adds code to
the participating classes that implements the asso-
ciation as a pair of list fields. The added code in
each class includes:
a. A wrapper to the constructor that initialize the
relation data and the AssocEnd object.
b. A getter for the association end.
3. The association object uses the factory functions
to return association ends and aggregators.
4. The AssocEnd and aggregator objects are initial-
ized with a reference to a list of associated in-
stances.
ENASE 2010 - International Conference on Evaluation of Novel Approaches to Software Engineering
102
5. Each AssocEnd object has onAdd and onRemove
methods, to handle special cases, such as caching
(see Section 3.3).
The module supports the following aggregators:
Serial traversal over associated instances.
Parallel traversal with K threads over associated
instances.
Maintaining a cache based on a given key function
and filtering instances using this cache.
4.2 Usage Example
Figure 19 shows two Python classes that represents
workers in a department. We would like to imple-
ment an association between the department and its
employees, so that each department will hold a set
of its workers. Figure 20 shows the code for creat-
ing the association object with default factory func-
tion. The command Emps.add(dept, emp) will asso-
ciate the given department and employee.
class Dept ( o b j e c t ): pass
class E m p loye e ( o b j e c t ):
def _ _ i nit_ _ ( self , id , name ):
self . id = id
self . name = nam e
def g e t Id ( self ):
return self . id
def h a s Id ( self , id ):
return self . id == i d
def g e t N a me ( s e lf ):
return self . n ame
Figure 19: Associated classes in Python.
Emps = Asso c _ n _ n (
cls1 = Dept , cl s 2 = Emplo y e e ,
name1 = worke r s , name2 = wor k s I n ,
fact o r y 1 = Li s t A s s o c E n dF a c t o r y ( Em p l o y e e ) ,
fact o r y 2 = Li s t A s s o c E n dF a c t o r y ( D e p t ))
Figure 20: An association object in Python.
When the association object is created, it add
the participating classes getter methods for the as-
sociation ends. In this case, it add the method
getWorkers() to the class Dept, and getWorksIn() to
Employee. The AssocEnd objects provide the aggrega-
tor operations. For example, the function in Figure 21
returns a worker in a department by id.
The association in Figure 19 uses the default im-
plementations. Therefore, the aggregator operations
are performed by iterating over the list of associ-
ated objects sequentially. Finding an employee with
a given id by scanning the entire list can slow down
the application if a department has many employees.
def d ep t W or k e r By I d ( dept , id ):
return dept . g etW o r k ers ()
. f i l t e r ( ). hasId ( id )[0]
Figure 21: Getting a worker in a department by id.
It is possible to solve this problem by replacing the
default factory function for filter operations. The as-
sociation shown in Figure 22 uses a Filter operation
with caching. As expected, our measurements shows
that the function from Figure 21 runs faster by an or-
der of magnitude with these settings. Note that no
change in this function or in the above classes is re-
quired to achieve this boost in performance. Simi-
larly, Figure 23 shows an association that uses mul-
tiple threads for some aggregator operations. These
settings can speed up batch I/O operations (for exam-
ple, each employee object writes something to a file),
also without changing any client code.
Emps = Asso c _ n _ n (
cls1 = Dept , cl s 2 = Emplo y e e ,
name1 = worke r s , name2 = wor k s I n ,
fact o r y 1 = Li s t A s s o c E n dF a c t o r y ( E m p loyee ,
fi l t e r Imp l = m ak e L is t Q u a l F i l t er (
Empl o y e e . getId , Employee . hasId )) ,
fact o r y 2 = Li s t A s s o c E n dF a c t o r y ( D e p t ))
Figure 22: An association with caching for employee ids.
Emps = Asso c _ n _ n (
cls1 = Dept , cl s 2 = Emplo y e e ,
name1 = worke r s , name2 = wor k s I n ,
fact o r y 1 = Li s t A s s o c E n dF a c t o r y ( E m p loyee ,
fo r e a chI m p l = m a k eL i s t S p aw n F or e a c h (5) ,
mapImp l = m a ke L i st S p aw n M ap (5)) ,
fact o r y 2 = Li s t A s s o c E n dF a c t o r y ( D e p t ))
Figure 23: An association with 5 threads for Foreach and
Map operations.
This module with its test code is available from
http://www.cs.bgu.ac.il/gwiener/software/associa-
tions.
5 CONCLUSIONS
In this work we showed a novel approach for gen-
erating code for associations. Rather than focusing
on a single, sequential, in-memory implementation,
we presented a flexible and open approach. Our ap-
proach is based on a set of interfaces that provide op-
erations over the entire relation. These operations in-
clude managing the relation through the Association
interface, and traversing it using Foreach, Map, Fil-
GENERATING FLEXIBLE CODE FOR ASSOCIATIONS
103
ter, and Fold. Traversals, excluding Fold, can be im-
plemented by a multi-threaded code. The Association
and Filter interfaces encapsulate optional optimiza-
tions, such as caching or using a database. By provid-
ing this encapsulation, the client code is independent
of the implementation details of the association.
5.1 Future Work
Our current work covers only the basic functionality
of associations I.e., to operate on the objects that
are associated with a given instance. It does not cover
the following advanced topics: 1) Association classes
2) Properties of associations, such as set and ordered
3) Relations between associations, such as sub-set
and override. We plan to include these topics in the
following iterations of our work. Specifically, we in-
tend to support relations between associations in our
scheme, so that adding or removing these relations
will not affect the client code.
ACKNOWLEDGEMENTS
This work has been prepared with the kind support of
the Frankel Center for Computer Science at the Ben-
Gurion University.
REFERENCES
Akehurst, D., Howells, G., and McDonald-Maier, K.
(2007). Implementing associations: UML 2.0 to Java
5. Software and Systems Modeling, 6(1):3–35.
Allen, J. (1978). Anatomy of LISP. McGraw-Hill Book
Company.
Blaha, M. and Premerlani, W. (1997). Object-oriented mod-
eling and design for database applications. Prentice-
Hall, Inc. Upper Saddle River, NJ, USA.
Bloch, J. (1995). The Java Tutorials: Collections.
http://java.sun.com/docs/books/tutorial/collections.
Fowler, M. (2003). UML Distilled: A Brief Guide to
the Standard Object Modeling Language. Addison-
Wesley Longman Publishing Co., Inc., Boston, MA,
USA.
Gamma, E., Helm, R., Johnson, R., and Vlissides, J. (1995).
Design Patterns: Elements of reusable object-oriented
software. Addison-Wesley Longman Publishing Co.,
Inc., Boston, MA, USA.
Gessenharter, D. (2008). Mapping the UML2 semantics of
associations to a Java code generation model. In Czar-
necki, K., Ober, I., Bruel, J.-M., Uhl, A., and V
¨
olter,
M., editors, MoDELS, volume 5301 of Lecture Notes
in Computer Science, pages 813–827. Springer.
Giacaman, N. and Sinnen, O. (2008a). Parallel iterator for
parallelising object-oriented applications. Technical
Report 669, University of Auckland.
Giacaman, N. and Sinnen, O. (2008b). Parallel iterator for
parallelising object oriented applications. In Proceed-
ings of the 7th WSEAS International Conference on
Software Engineering, Parallel and Distributed Sys-
tems, pages 44–49. World Scientific and Engineering
Academy and Society (WSEAS).
Harrison, W., Barton, C., and Raghavachari, M. (2000).
Mapping UML designs to Java. ACM SIGPLAN No-
tices, 35(10):178–187.
Iverson, K. E. (1962). A Programming Language. John
Wiley & Sons, Inc.
Larman, C. (2004). Applying UML and patterns: an in-
troduction to object-oriented analysis and design and
iterative development. Prentice Hall PTR Upper Sad-
dle River, NJ, USA.
Martin, J. and McClure, C. (1983). Software Maintenance:
The Problems and Its Solutions. Prentice Hall Profes-
sional Technical Reference.
Martin, J. and Odell, J. J. (1992). Object-oriented analysis
and design. Prentice-Hall, Inc., Upper Saddle River,
NJ, USA.
OMG (2005). OCL 2.0 Specification. Formal specification,
Object Management Group.
Pigoski, T. M. (1996). Practical Software Maintenance:
Best Practices for Managing Your Software Invest-
ment. John Wiley & Sons, Inc., New York, NY, USA.
Rumbaugh, J., Jacobson, I., and Booch, G. (2004). Uni-
fied Modeling Language Reference Manual, The (2nd
Edition). Pearson Higher Education.
Warmer, J. and Kleppe, A. (2003). The Object Con-
straint Language: Getting Your Models Ready for
MDA. Addison-Wesley Longman Publishing Co.,
Inc., Boston, MA, USA.
ENASE 2010 - International Conference on Evaluation of Novel Approaches to Software Engineering
104