Implications on the Event Model from Physical Coupling

Marc Paterno, Brent May

Below we discuss ramifications of physical coupling on the Event model along with concrete examples of some design choices.


Why reduce physical (and logical) coupling in the Event Model?

Since the Event model will evolve over time as new data types and algorithms are developed, we would like changes to the Event model to not cause clients (classes which use the Event class) to require recompilation unnecessarily. In a poorly physically designed Event model, adding a new data type to be stored or a making tiny change in a low level class (e.g. Jet) could cause all clients of the Event class to require recompilation, which would slow development and testing to a crawl. Changes to the Event Model that can cause clients to require recompilation include:

In addition, even if compile-time coupling is kept low, the Event model could cause large link-time coupling so that using or testing a low level class could require linking with a significant number of classes in the Event Model (and with other classes, perhaps not normally thought of as part of the "Event Model"). This is the situation we had in Run I, in which testing involved linking the entire system, rather than small units. In C++, link-time coupling occurs when some part of the Event model uses the interface or implementation of a class, rather than knowing about the class in name only (see below for a definitions of terms, taken from "Large Scale C++ Software Design" by John Lakos. For more information on physical coupling, read this book).


Example of changing Event class interface or implementation causing clients to recompile

Any change in the Event class's interface or implementation as contained in Event.h, causes all clients of Event to require recompilation.

Event.h:

// Version 1: Event contains Tracks and Vertices only
// Version 2: added PreshowerHits data and accessor function
// Version 3: added getTracksInVertex accessor

class Event {
public:
   Tracks* getTracks() const;               // V1
   Vertices*  getVertices() const;          // V1
   PreshowerHits* getPreshowerHits() const; // new in V2 (all clients of Event class must be recompiled)
   Tracks* getTracksInVertex() const;       // new in V3 (1 day later...recompile again)
private:
   Tracks* _tracks;
   Vertices* _vertices;
   PreshowerHits* _psHits;                  // new in V2 
}



Discussion

One way to insulate clients from this sort of change is to add layers between the Event class and the data it contains and provide accessors only to the sublayers rather than the data itself. This layered approach can be use at several different levels create a highly modular and decoupled Event Model.

Another solution is for the Event class to contain a pointer to a generic data type which is "cast" to the correct type (at least, one hopes it is cast to the correct type!). In this way clients of the event are insulated from the specific types of data held by the Event class and conceivably only one accessor is ever needed.

Example of physical coupling in the Event causing clients to recompile or link when a contained item is changed

If Event.h needs headers (like Tracks.h) for the data it holds, any change in those headers will cause all clients of the Event class to require recompilation. Also, if either the header file for Event or implementation file (Event.cc) needs headers, all clients of Event must be linked with the object file (like Tracks.o), even if the client never uses the class.

Event.h:

class Event {
public:

 // Changing the definition of Tracks does not cause the Event class 
 // or its clients to require recompilation. However, it could cause a link-
 // time dependency if a member function of Event (1) uses the Tracks class 
 // interface or (2) returns a Track by value (not pointer or reference)

   Tracks*  getTracks1();  // Neither ptr nor ref force inclusion of Tracks.h.
   Tracks&  getTracks2();  // Neither causes a link dependency: Event.c doesn't need Tracks.h
 
   Tracks   getTracks3();  // Doesn't require Tracks.h. 
                           // Link dependency from return by value in Event.c
 
   float getATrackPt();    // Causes link dependency due to Event.c needing Track.h
 
   float getATrackZ()      // Inline function requires Track.h: Compile & link dependency
      {return _trks1->track(1)->z();}
  
private:

// In member data, pointers and references do not cause compile-time and 
// link-time coupling, but containing by value typically does.
  
   Tracks* _trks1;           // Doesn't require Tracks.h 
   Tracks& _trks2;           // Doesn't require Tracks.h
   Tracks _trks3;            // Requires Tracks.h
   RefCntPtr<Tracks> _trks4  // Requires Tracks.h due to template instantiation
   VertexList _vert;         // Requires Vertex.h (which requires Tracks.h)
}


Discussion

Member functions of the Event class can return objects by value, pointer or reference, without causing a compile-time dependency, but returning an object by value (like Tracks) requires that the Event link with the component object file (Tracks.o). Inline functions typically expose implementation detail in the header file thus causing compile and link dependency.

Member data contained by pointer or reference causes no compile or link dependency, but containing by value causes both a compile and link dependency. In addition, containing by value can cause a cascade of dependencies to many other classes.


Why is physical coupling a potential issue with the event design?

In short, the smallest change in the Event class interface or attributes can cause all clients (classes, functions, etc.) that use the event interface to require recompilation. This is called a compile-time dependency or coupling. Thus adding a list of muon clusters to the event would provoke recompilation of all reconstruction code and analysis code that used the Event class.

Another aspect is that any client that uses any part of the Event interface may require all classes contained in the event to link. This is called link-time dependency. A variation of this is that any client that uses a class (e.g. Jet) independent of the event may still require all classes within the event to link if the class uses the Event in name (not the interface). A severe variation is the cyclic link-time dependency, in which one class (or a small set of classes) cannot be tested without linking in all the others in the cycle.

Some terminology

class declaration - introduces a class name into the program
class definition - introduces a class description (i.e. full implementation).
Uses in the interface - the class uses the type in the interface of a public member function (includes IsA)
Uses in the implementation - class uses that type in its (private) implementation includes HasA, HoldsA, WasA)
IsA - the class publicly inherits from the type
HasA - the class has an instance of that type
HoldsA - the class has a pointer (or reference) to the type
WasA - the class privately inherits from the type
Component - smallest unit of physical design (a header/implementation file pair)
DependsOn - the component x needs a component y to compile or link
Compile-time dependency - if y.c needs x.h to compile
Link-time dependency - if y.o has undefined symbols that x.o is needed to resolve at link time
Uses in the implementation - refers to that type by name anywhere in the component.
Uses in the interface - type is used in the public/protected interface of any class defined or any free/operator function declared in the .h file for that component
Uses in name only - if compiling the component and any of the components on which it depends does not require first having seen the type (.h file). That is, holds its address (pointer/reference) and never interacts with the object.
Uses in size - if compiling the component requires first having seen the definition of the type (.h file)

Back to Run II Event Model/Algorithm Considerations
Back to Run II Event Data Model Home Page
Back to Back to DØ Home Page

This page is kept by Marc Paterno (paterno@fnal.gov) and Brent May (bjmay@fnal.gov)

Last updated 4 April 1997.