Let us look at some simple composition here and discuss what
the big deal is all about. To start, here are what I believe to be the
three most important and beneficial things about using composition.
- Black Box design (where inheritance is white box design)
- Does not break encapsulation
- Relationships can be defined at runtime
Black box design means that the client using the object does
not know the internal operations. This is in contrast to inheritance, where
the class often knows the internals of its super class. Not breaking
encapsulation is as good a reason to use composition as any. After all,
encapsulation of operations and data is one of the main reasons to use object
orientation. And lastly, "relationships can be defined at runtime"
means that other objects can be plugged into, or composed with, other objects
while the program is executing. This is a common use of the Strategy pattern,
where an algorithm is set to the class at runtime.
Looking at an Example of Composition
Below, you will see a snapshot of part of a fictitious
product component. ProductBase is composed of StockStatus, which in turn
is composed of PurchaseOrderLine and IRunRate. Here, StockStatus does not know
about the implementation of IRunRate, it only knows IRunRates interface.
Here, we can then substitute any other object of type IRunRate into StockStatus
and it would be none the wiser. You could of course, change behavior by having
StockStatus be aware of its algorithms and change depending on what it is
composed of - but that is not taking advantage of composition.
Figure 1 - UML Diagram
PurchaseOrderLine is the next object. Although
StockStatus is not connected to an abstract class or "interface"
type, the public methods and properties of PurchaseOrderLine make up its
interface. So, again, StockStatus only knows about a well defined