Composition is a constructive relationship forming a tightly bound whole-part hierarchy. It achieves strong or tight binding by embedding or nesting the part objects inside the whole. This organization is a small step beyond the simple classes we created in the previous chapter. For example, consider the Time class that represents time as three variables: hours, minutes, and seconds. When a program instantiates the Time class, it creates an object with sufficient memory to store three integers, one each for the hours, minutes, and seconds. Ignoring some detail for a moment, we only need to change the "int" datatype to a class name to create a composition relationship.
class Time
{
private:
int hours;
int minutes;
int seconds;
};
Time t;
Class Specification
Defining/instantiating An Object
Abstract Representation
The Time class and object. We previously analogized the relationship between a class and an object as a cookie cutter and the cookies we make from it. The cookie cutter describes a cookie's size, shape, and decorations but is inedible. Alternatively, the cookies are the real treat that we eat. More formally, a class is a declaration and a type specifier. The object is a specialized variable created by a definition statement.
UML Classes
Composition Classes
Abstract Representation
class Engine
{
. . .
};
class Transmission
{
. . .
};
class Car
{
private:
Engine cars_engine;
Transmission cars_transmission;
. . .
};
Building a whole-part object with composition. In this example, a Car (the whole) consists of an Engine and a Transmission (the parts). We implement the whole-part relationship with two member variables (highlighted with blue and red) in the Car class. When a program instantiates the Car class, it simultaneously instantiates the Engine and Transmission classes and embeds the objects inside the Car. As composition is a whole-part relationship, we can say that "a Car has-an Engine" or that "a Transmission is part of a Car." However, nesting or embedding the parts inside the whole suggests that we can also read the relationship as "a Car contains an Engine" or "a Car contains a Transmission."
Although the Engine and Transmission details are not significant for this example, their class specifications must precede the Car specification. If we specify the classes in a single file, we must do so as illustrated here. Specifying the classes in separate header files requires the program to #include the part header files before specifying the whole. The next sections describe how to initialize and use part objects embedded in a whole object.
Composition Summary: Filling In The Table
Composition is a constructive relationship conveniently characterized by the "has-a" phrase. It has many property values in common with aggregation but differs in the strength or tightness of the binding between the objects. The following figure summarizes composition's property values. Use the summary to check and complete your entries in one of the blank Class Relationship Tables located at the end of the chapter.
Class Roles. The relationship forms a class hierarchy. The first class plays the role of the Whole class while the second is the Part.
Semantics. We can read composition in either direction. We read it from the Whole to the Part as a has a relationship and from the Part to the Whole as a part of.
a Car has-an Engine
an Engine is part of a Car
a Car has-a Transmission
a Transmission is a part of a Car
Directionality. Composition is a unidirectional or a one-way relationship. Unidirectionality means that
(a)
(b)
(c)
(d)
The operations may only take place in one direction: from the whole to the part
The whole object can send a message to the part object, which can respond to the message but cannot initiate message sending
The whole "knows" about the part, but the part doesn't "know" about the whole
It is possible to navigate from the whole to the part object but not from the part to the whole
Binding Strength. The binding between the two objects is very strong or tight because the part object is embedded inside the whole object (Figure 2). The strength of the binding implies the final two characteristics:
Lifetime. The two objects have a coincidental lifetime, which means that the two objects are created and destroyed at the same time. It also means that the relationship between the two objects is permanent - it cannot be altered or broken.
Sharing. The binding between the two objects is so tight or strong that it forms an exclusive relationship - the whole object does not share its part object with any other object in the program.
C++ implementation.
class Car
{
private:
Engine my_engine;
Transmission my_transmission;
};
Composition property values. Like all constructive relationships, C++ implements composition with class-scope (i.e., member) variables.