Mapping Models to Code

References:

  1. Bernd Bruegge and Allen H. Dutoit, "Object-Oriented Software Engineering", Third Edition, Chapter 10

10.1 - Introduction: A Book Example

10.2 - An Overview of Mapping

10.3 - Mapping Concepts

10.3.1 - Model Transformation

10.3.2 - Refactoring

10.3.3 -0 Forward Engineering

10.3.4 - Reverse Engineering

10.3.5 - Transformation Principles

10.4 - Mapping Activities

10.4.1 - Optimizing the Object Design Model

Direct translation of analysis models to code is often inefficient. This section describes four common optimizations to improve performance or meet other design goals.

Optimizing access paths.

Repeated association traversals. Operations that are performed frequently can suffer from poor performance if a large number of associations must be traversed. Performance can be enhanced by adding a direct link, ( at the cost of additional overhead to create and maintain the new link. ) Examine sequence diagrams of frequently performed operations to identify long association paths.

"Many" associations. If a lot of time is being spent working down a list of associations in a one-to-many or many-to-many situation, see if the to-many can be reduced to a to-one, possibly through qualified associations. Alternatively, order or index the associations to make finding the desired one faster.

Misplaced attributes. Some classes may turn out to have very little if any interesting behavior. If an attribute is involved only in setter and getter operations, consider moving the attribute to the class that calls it. If enough of the attributes can be moved out of the class, it can sometimes be eliminated all together. ( See next section. )

Collapsing objects: Turning objects into attributes. A class that has few attributes and behaviors, and is associated only with one other class, can be collapsed into that class, reducing the overall complexity of the system.

Delaying expensive computations. Put off performing expensive calculations until they are really needed.

Caching the result of expensive computations. Sometimes it is more efficient to save the result of expensive calculations, rather than re-doing the calculations every time the result is needed. The tradeoff is the additional variable(s) that must be stored and maintained. It is also wise to combine this idea with the previous one, so that the expensive calculation is only performed when the result is needed, and the result cached for future re-use. If the inputs to the expensive calculation change, then the cached value should be invalidated, but not re-calculated until the result is needed.

10.4.2 - Mapping Associations to Collections

Unidirectional one-to-one associations. In the example below, each Account is associated with exactly 1 Advertiser, and the only traversal of the link is from the Advertiser to the associated Account. In this case the Account is created by the Advertiser constructor, and the Advertiser retains a link to the Account, but not vice-versa:

Bidirectional one-to-one associations. As above, but now there is a link from the Account to the Advertiser as well as the other way around. The constructor for the Account is still called as a byproduct of constructing an Advertiser, but now it contains a link back to the Advertiser with which it is associated:

One-to-many associations. If the Advertiser can have multiple accounts, then the Advertiser needs to have some sort of collection ( Set, List, HashMap, etc. ) to refer to the Account(s). The Account is no longer created by the Advertiser constructor, but rather must be created separately and added to the Advertiser as a separate step. Note that the code below has a problem if Advertiser.addAccount( ) is called before Account.setOwer( ).

Many-to-many associations. Now both ends of the association need to use collections, with appropriate checks to maintain the consistency of the associations, and to prevent circular recursion. ( The example above needs this check in Advertiser.addAccount( ). )

Qualified associations. The idea behind a qualified association is to reduce a to-many association into a to-one association. For example, a directory can contain many files, but it can only have one with a given filename. In the example below, each Player is given a nickname that is unique within a given League. Now a League can have multiple players, but only one with any given nickname. Note that the collection has now been changed from a Set to a Map. ( League names are also unique, which makes the to-one relationship work from Player to League as well as League to Player.

Associations classes.

10.4.3 - Mapping Contracts to Exceptions

10.4.4 - Mapping Object Models to a Persistent Storage Schema

Mapping classes and attributes

Mapping associations

Buried associations.

Associations can be kept simply by having a field in one table refer to a field in another. Note in the following that the association can only be reconstructed from the League table to the LeagueOwner table, and not the other way around. However both associations ( bidirectional ) can be established when the relevant objects are created.

Separate table.

Alternatively a separate table can be used to record all associations between members of two classes. This is necessary in the case of many-to-many associations.

Mapping inheritance relationships

There are two main options for storing inherited data in tables.

Vertical mapping.

  • The parent class can have its own table, where all the attributes defined in the parent class are stored for all objects of either the parent or derived classes.
  • A field in the parent class refers to additional information for each object found in the descendant class tables.
  • Note that the ID field is unique for the same object across multiple tables.

Horizontal mapping.

  • Alternatively the derived class tables can include all attributes for the object, whether inherited or derived.
  • This causes the same field to be recorded in multiple tables, but since any given object only appears in a single table, it does not really take up any more space.
  • The parent class table can now be eliminated, unless objects are created which are purely of the parent class.

10.5 - Managing Implementation

10.5.1 - Documenting Transformations

When transformations are made to code, ( refactoring ), inconsistincies can easily develop between the design model and the code implementation model. Over time these consistencies can become significant, to the point where the existing design model no longer reflects the implemented code accurately.

Reverse engineering attempts to re-create a design model that matches the current implementation, but information gets lost in this process through several reasons:

The challenge then is to retain consistency between the design model and the implementation, and to retain full design information, through all transformations. The following principles help, when applied consistently:

10.5.2 - Assigning Responsibilities

10.6 - ARENA Case Study

10.6.1 - ARENA Statistics

Statistics ( counters ) are to be maintianed for six scopes:

Statistics objects are to be produced by the Game Abstract Factory

A default Statistics class is provided for Games that do not require any special statistics.

Statistics is related to the other classes as an n-ary association, as shown below. The relationship is that a given Statistics object is associated with exactly one of ( a Game or a League or a Tournament ), and may or may not also be associated with a Player. One Game ( or League or Tournament ) only has a single Statistics associated with it, but a Player may have multiple associated Statistics, one for each Game / League / Tournament with which (s)he was involved.

10.6.2 - Mapping Associations to Collections

The StatisticsVault class is introduced to maintain the associations between Statistics objects and the things they are associated with. As first designed this requires two calls to get staticstics data - One to the Vault to acquire a Statistics object, and a second to get the data.

Reworking this as a Facade pattern simplifies the interface and requires only a single call to access Statistics.

10.6.3 - Mapping Contracts to Exceptions

10.6.4 - Mapping the Object Model to a Database Schema

Because each Statistics object is associated with EITHER a Game, League, or Tournament, the associations can be combined. In the first table, the ID is the number of the Statistics object, the scope is a reference to the ID of the Game, League, or Tournament with which is is associated, and the scopetype indicates which of the three types of associations it is, i.e. which separate table the scope field refers to. The Statistics table holds the association information, and the StatisticCounters table holds the actual values.

10.6.5 - Lessons Learned