These are the two main benefits that the Composite pattern provides:
You can treat the whole collection of objects the same way you would treat any of the individual objects in the collection. Functions executed on the composite are passed down to each of the children to be executed. On large collections, this becomes very beneficial (though also may be misleading, because you may not realize how large the collection is and therefore not understand how much performance may suffer).
It organizes the objects into a tree structure, and since each composite object contains a method to get its children, you can hide the implementation and organize the children in any way you wish.
In the Composite patterns hierarchy, there are two types of objects: leaf and composite. The image below shows an example of the Composite’s structure. It is recursive, which is what gives this pattern its power. The difference between the composite objects and the leaves are that leaves do not have any children, while the very essence of a composite is that it has children.
There a number of somewhat common examples of the Composite pattern. If you’ve ever used a PC, you’ve more than likely seen a frequently used implementation of this pattern: the file structure. Consider every disk/drive and folder to be a composite object and every file to be a leaf. When you try to delete a folder, it’ll not only delete that folder, but also every other folder and file contained within it. You really don’t get a much better example than that.
Another example that is a special type of composite is the Binary Tree. If you do not know what that is, you may want to see this Wikipedia article on the Binary Tree. It is special because each node can contain at most 2 children. Also the leaf and composite pieces are exactly the same. Composites represent an end value, just like the leaves and the leaves can become composites at any time by giving them children. It is also optimized to be primarily used to search through sortable data.
Notice that in my image, all of the images are contained within composites on the “Gallery” level. This is by no means necessary, nor will it be enforced in the code, but it is ideally how the images are to be organized.
||add a child node to this composite|
||remove a child node from this composite (or one in a deeper level)|
||returns a child object|
||hides the composite and all of its children|
||shows the composite and all of its children|
||get the HTML element of the node|
Next we’ll implement the images using GalleryImage:
Notice that the GalleryImage class doesn’t do anything in the
getChild functions. Since it is the leaf class, it doesn’t contain any children, so it doesn’t do anything for these methods. However, we do need to include those functions in order to comply with the Interface we set up. After all, the composite objects don’t know that it’s a leaf and might try calling those methods.
You can see the amazing benefits of always being able to just call a function on a top level object and have the results happen to any or all of the nodes in the composite structure. The code becomes much easier to use. Also the objects in the composite are loosely coupled because they all just follow the same interface. Finally, the composite gives a nice structure to the objects, rather than keeping them all in separate variables or in an array.
However, as mentioned earlier, the composite pattern can be deceptive. Making a call to a single function may be so easy that you might not realize the adverse effect it’ll have on performance if the composite grows rather large.