Of course, I am not the only one. If you are familiar with our friends, the Gang of Four, you have already heard this mind-bending concept. Make your objects connect to each other via well defined interfaces and hide their implementation. This is a sound, modern software practice. In fact, I just spent four days this past summer listing to speakers take composition for granted at the architectural level. They were assuming (not all of them of course) that composition and abstraction is a skill that is taken for granted by most developers. We know, being the folks in the trenches, that this is not always the case.
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 interface.
It is important to look at your design and try to understand the moving parts. These will let you know what the system will be composed of. In my next article, I will talk more about identifying the objects that make up your system, as well as why this design used an abstract class at its base and built upon it with composition.