The Composite Pattern is one that is not taken advantage of as often as it might outside of frameworks. This is an approach to design that allows you to treat a family of objects the same. It is a quintessential reason for and use of an interface. The flexibility is critical to being able to handle groups of different objects the same way any individual one would be treated.
The Composite Pattern Defined
As always, we will start with the “Gang of Four” intent to set the stage for our discussion.
“Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.”
I think it may be most comfortable to focus on the second sentence in defining this pattern. The tree structure feels a bit like an implementation detail rather than an abstract pattern. The tree structure reference is often (if not always) seen in utilizing this pattern. It usually breaks down to a concept of a container that holds other containers or primitives. Graphics are a perfect example. You have pixels, but then those can be combined into shapes or other container structures.
Applying The Pattern
The key to this pattern is an interface or top-level class. It is more flexible to use an interface for this implementation, but you may find cases where a top-level class reduces code and allows for re-use. The objects that implement the interface will either perform the action on themselves or have a way to execute the function on a collection of objects.
For example, a square can be seen as a collection of four lines. When you “draw” the square it will tell each of the four lines to draw themselves. Each line is likely a collection of points. Therefore, each line will instruct its points to draw themselves. A point ends up doing the real work; everything else is a delegation. This simple example can be extended to consider a square that also draws its center point. That would be a collection of lines and a point. Although it seems simple, the ability to mix and match primitives and collections has a broad range of uses.
Java, PHP, C#, etc.
All modern languages support an interface and thus look very similar in implementation. You create an interface that contains the methods you want to be shared and then create the classes that implement it. The implementation of a method in a class can either be handled within the class or passed through to objects it contains or controls. We will see some other patterns that are similar in that they make it easy to apply functions across a heterogeneous collection.