Design Pattern Evangelist Blog

Smart pointers about software design

Composite Design Pattern

Configure behavior emerging from a group of snippet behavior objects organized in a tree structure.


Jet Engine Cutaway

Introduction

The Composite Design Pattern is the next of the Composable Design Patterns series.

The Composite Design Pattern embodies the concept of composable design patterns more completely than the previous patterns we’ve seen. Several concepts first introduced in Composable Design Patterns will be revisited here as well.

Let’s review the composable patterns we’ve seen so far:

The previous composable design patterns are based upon a linear structure. They use single delegation or a list as their data structure mechanism. Each tends to focus upon one core feature class, which is enhanced by other supporting classes composed around it.

Composite is almost exclusively about composition. There is no single core feature class. Composite features multiple classes with each providing a snippet of behavior. Objects implementing those snippets of behavior are composed into a core behavior that’s emerges from an aggregation of those object snippets organized via Composite. There could be many behaviors depending upon the organization of the object snippets.

Composite breaks the shackles of a linear structure. Composite’s underlying data structure is a self-referential tree. One codebase can accommodate any number of Composite object trees that are as wide or as deep as needed for each given situation. A Composite tree could be one object or thousands of objects.

Composition is not a foreign concept in OO. It’s the HAS-A relationship, as in:

HAS-A relationships are often rendered in OO programming language as a private field attribute class or a collection of classes. The other primary OO relationship is IS-A, which is traditionally rendered with inheritance.

Just as Decorator is a dynamically composable alternative to rendering IS-A using inheritance, Composite is a dynamically composable alternative to traditional rendering of HAS-A relationships in OO design.

Traditional HAS-A implementation tends to be statically locked into place at compile time as hardcoded field attributes. There is nothing wrong with this technique, especially with a domain for which these relationships do not change. Cars will always have engines. Chordates will always have spines. I often used this technique.

But sometimes HAS-A relationships aren’t always set in stone. They may vary. They may not be known until runtime. These are the situations where Composite may be more valuable for HAS-A relationship designs than traditional OO practices.

Real World Analogies to Composite

Like the previous composable design patterns, Composite is not difficult to implement. In fact, its implementation is almost trivial. Its main challenge is comprehension. Here are a few real-world examples to ease one into the Composite concept.

Jet Engine

I featured this jet engine cutaway at the top. It’s a sophisticated piece of machinery. I’m not a mechanical engineer, so I asked ChatGPT to list some parts of a jet engine: Jet Engine Cutaway

Imagine deconstructing a jet engine completely. It will be a collection of parts such as blades, metal plates, nuts, bolts, gaskets, etc. It will cease to be an engine. Most of the parts listed above will cease to exist as well. There will no longer be a Compressor or Turbine. It will just be the parts that used to be configured in such a way that they had the properties of a Compressor or Turbine.

This is akin to the old joke that an airplane is 10,000 loose parts flying in close formation.

I would probably model a jet engine more traditionally with: JetEngine HAS-A Compressor, JetEngine HAS-A Turbine and even more decomposition with Turbine HAS-A set of Blades, etc. rather than Composite, since the composition of a Jet Engine is not likely to change frequently.

The purpose of my example is to highlight the compositional HAS-A nature of a Jet Engine regardless of how one might model it using a traditional OO approach or Composite. The Compressor and Turbine really do not exist as an entity. Heck, even the Jet Engine does not really exist. They are the compositional sum of their components in a specific configuration. While we often refer to this relationship as HAS-A, it’s closer to IS-COMPRISED-OF.

Unix Filesystem

Unix Filesystem Layout Example

The Unix Filesystem is organized as a tree structure. Files are terminal nodes. Directories are non-terminal nodes, which can contain files, directories or nothing. But it’s more than that. Directories are files too. In Unix, everything is a file.

The self-referential filesystem structure, where directories can contain other directories, means that a command issued in a directory can propagate through all directories and files composed within the directory from which the command was executed especially if the recursive -r option was provided.

The directory concept is not limited to Unix, but it may have different names, such as Folder on a Mac or on Microsoft Windows

Logic Gates

Full Adder Logic Gates

The Composable Design Patterns blog featured an image of logic gates for a four-bit adder.

A Multi-bit Adder is a composite of Full Adder components, which are each a composite of AND, OR and NOT logic gates. The Multi-bit Adder and the Full Adders are composite configurations of these three simple logic gates. This applies to almost all logical components of a computer. They only have meaning as a concept because we choose to assign them meaning based upon the behaviors that emerge from their configurations.

Lego Brick Sets

Lego has been a popular toy for decades. The bricks were mostly basic when I was a child. Since then, Lego has expanded to specific themed sets often tied in with marketing and merchandizing of other brands, such as Star Wars themed Lego sets.

There are hybrid sets too. My son had a Police Boat set which featured a specific configuration on the front cover of the box, with step-by-step instructions on how to build the featured Police Boat. But the back cover provided photos of alternative boats that could be assembled from the same bricks. Other than the photos, these alternative boats were left up to the child’s imagination.

No individual Lego Brick has much agency. Form doesn’t emerge until the bricks are snapped together to create a cohesive structure.

Lego Millennium Falcon

You embark upon a new Lego project. You start with thousands of individual bricks which will eventually become the Millennium Falcon. They are assembled in stages. Individual bricks are components assembled into composite ship parts. These composite parts are assembled into even larger composites. Eventually the composites become identifiable parts of the ship, such as the cockpit, landing gear, engines, etc. After hours of assembly, the final parts are snapped together, and you have the Millennium Falcon.

Lego Patent

Even though the individual bricks have different shapes and sizes, they follow the consistent Lego snapping mechanism, which has been their common interface since the beginning. All bricks, regardless of their sets, can snap together with other bricks. There are even apps that will use AI to create new projects with instructions from a photo of random bricks.

These properties aren’t unique to Lego Bricks. They apply to many construction toys including:

Cooking

One of my first blogs featured cooking – Knock Knock … Who’s There?

Meals are a composite of dishes, which are a composite of ingredients, which are a composite of molecules, which are ….

How far do we take the decomposition of components into composites? Where do we stop?

“If you wish to make an apple pie from scratch, you must first invent the universe.” – Carl Sagan.

With all deference to Carl Sagan, we don’t have to go that far as we’ll soon see.

Life to Chemistry

Elements of the Human Body

Complex living organisms are composites of systems, such as: nervous system, circulatory system, pulmonary system, etc.

Systems are composites of tissues, such as: nerves, muscle, arteries, etc.

Tissues are composites of cells.

Cells are composites of organelles.

Organelles are composites of molecules.

Molecules are composites of atoms.

Much like an airplane being thousands of loose parts flying in tight formation, the human body is an unfathomable number of loose atoms composed in tight formation.

Turtles All the Way Down

But why stop with atoms in the Chemistry/Biology example? Atoms are composites of protons, neutrons and electrons. Protons are composites of quarks.

Who knows? Maybe quarks and other subatomic particles are composites of other components not yet identified.

Composite’s self-referential tree structure means that we can easily ride these composite turtles a long way down ad absurdum.

Sagan Apple Pie Recipe

One could argue that since most of the previous composite examples are based upon materials in the real world, they all could be reduced to subatomic particles. But unless we’re writing software for the particle collider, we’re probably not concerned with subatomic particles in our models.

We stop when we’ve reached behavior that’s a single behavior snippet within our domain.

While Carl Sagan may be technically correct, the first step of an Apple Pie recipe doesn’t necessarily need to be: Trigger the Big Bang.

Design Structure

Composable Design Patterns Template

Composite’s structure is the foundation for the other Composable Design Patterns.

When I presented an image for Composable patterns in Composable Design Patterns, I had to be careful not to present Composite itself.

Here is the Gang of Four’s (GoF) Composite design:

GoF Composite Design Pattern

One more item. Where should “add” reside in the design? From the GoF:

Declaring the child management operations. Although the Composite class implements the Add and Remove operations for managing children, an important issue in the Composite pattern is which classes declare these operations in the Composite class hierarchy. Should we declare these operations in the Component and make them meaningful for Leaf classes, or should we declare and define them only in Composite and its subclasses?

They argue that structural management placement is a tradeoff of transparency versus safety. Placing it in Component provides uniformity, but at the safety expense that someone may try to add component to a Leaf object.

Placing structural management in the Composite is safer, but now the structure management feature is missing from the main Component interface.

I feel that safety is more important than transparency, which is why my diagram places add(Component component) in the Composite. Here are my reasons:

Here are some examples of what the Composite object tree might look like. This first version is the simplest. The tree consists of one leaf node:

GoF Composite Design Pattern Objects One Node

Here’s a more complex tree with several composite and leaf nodes:

GoF Composite Design Pattern Objects Tree

In both examples, the client only has a reference to the root of the tree.

Use Case

The Use Case is too large to include in this blog. It will be posted in the next blog.

Composite Pros and Cons

The relative pros and cons of Composite are like those with most of the Composable design patterns.

Pros

A small implementation can support many composable configurations. The objects in the composition can be as few or many as needed for the desired behavior. Each composable configuration features its own specific behavior.

Constructing the object composition is relatively easy. While the Configurer is mostly independent from the design pattern in its traditional form, the Configurer is still the brains that’s responsible for the correct composition when using this design pattern. There are at least three types of Configurers and possibly more:

Cons

I almost want to write that there’s no cons for Composite. I don’t think there are any cons in the implementation, but there’s a potential con with its intent.

Composite is all structure. Behavior derives from its composition, which technically resides in the Configurer, which resides outside of the design. Composite will support any structurally consistent composition, even those that don’t make logical sense.

Anyone can grab random Lego blocks and snap them together. Logic gates can be wired together in any number of ways without providing any useful behaviors.

Behavior is in the eye of the Configurer, and the Configurer may need glasses. The Configurer could be a self-servicing customer/user who configures something that they think is correct, but it is not correct. We have all written code with software bugs. Configurers can just as easily configure composites with logical bugs in their structure. These Configurers will first accuse your implementation of containing a bug before admitting that the error resides in their configuration. The fault, dear Brutus, lies not in our stars, but in ourselves.

Integration and acceptance testing won’t address these configuration issues either since they’re a creation of the user.

Summary

Composite allows us to compose snippets of behavior into a tree structure, such that behavior emerges from the shape and organization of the tree more than from any individual node.

References

There are many online resources with diagrams and implementations in different programming languages. Here are some free resources:

Here are some resources that can be purchased or are included in a subscription service:

Comments

Previous: Chain of Responsibility Design Pattern

Next: Composite Design Pattern - Use Case

Home: Design Pattern Evangelist Blog