In the application domain, composition means has-a. In the implementation domain, it means is-implemented-in-terms-of.
Composition is the relationship where objects of one type contain objects of another type. It’s also known as layering, containment, aggregation, and embedding. As public inheritance means “is-a”, there’re are two meanings for composition:
- “has-a” in the application domain (where we model things in the world with objects, such as people, vehicles, video frames, etc.)
- “is-implemented-in-terms-of” in the implementation domain (where the objects are purely implementation artifacts, e.g., buffers, mutexes, search trees, etc)
While the has-a relationship is easy enough to understand, the distinction between is-a and is-implemented-in-terms-of is sometimes troublesome. For example, suppose we’d like to (re)use the list
template in the standard C++ library to represent fairly small sets of objeccts, i.e., collections without duplicates.
In the first glimpse, we may consider the following implementation:
|
|
As item 32 explains, if D is-a B, everything true of B is also true of D. However, a list object may contains duplicates, but a Set may not contain duplicates. It is thus untrue that a Set is-a list, and public inheritance is the wrong way to model this relationship.
The right relationship between the Set and the list should be is-implemented-in-terms-of:
|
|
|
|
As above shows, Set’s member functions can lean heavily on functionality already offered by list
and other parts of the standard library. Since they are so simple, they make reasonable candidates for inlining (refer to item 30 before confirming doing so).