Design pattern "Composite"

Let's learn what is the "Composite" design pattern 🌳🌿🌿
Monday, July 8, 2024

Real world example

Let’s suppose that we have a file structure within a directory and we are interested in determining the size of the root directory. To accomplish this, we need to traverse each subdirectory and recursively calculate the size of each one.

While it might seem straightforward to use a foreach loop for this task, it’s actually quite complex. This is where the Composite design pattern comes into play and proves to be very useful.

Loading graph...


In the Composite Pattern:

  • Leaf represents the end object of a composition. In our case, a file can be considered a leaf.
  • Composite is an element that has sub-elements (leaves or other composites). In our case, a directory is a composite.
  • Component is a common interface for all elements in a composition (i.e., it is implemented by both Leaf and Composite).

In plain words

Composite pattern lets clients treat the individual objects in a uniform manner.

Wikipedia definition

In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to β€œcompose” objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

Programmatic example

import java.util.ArrayList;
import java.util.Arrays;
// Component
interface FileComponent {
public int getTotalSize();
}
// Leaf
abstract class File implements FileComponent {
private String name;
public File(String name) {
this.name = name;
}
abstract public int getTotalSize();
}
// Composite
class Directory implements FileComponent {
private String name;
private ArrayList <FileComponent> children;
public Directory(String name, ArrayList <FileComponent> children) {
this.name = name;
this.children = children;
}
@Override
public int getTotalSize() {
int total = 0;
for (FileComponent child: children) {
total += child.getTotalSize();
}
return total;
}
}
// ---
// Leaf
class TextFile extends File {
public TextFile(String name) {
super(name);
}
@Override
public int getTotalSize() {
return 1;
}
}
// Leaf
class VideoFile extends File {
public VideoFile(String name) {
super(name);
}
@Override
public int getTotalSize() {
return 3; // We can imagine that video files are heavier
}
}
class Main {
public static void main(String[] args) {
// Create the tree structure
FileComponent root = new Directory("root",
new ArrayList <FileComponent> (Arrays.asList(
new Directory("directory",
new ArrayList <FileComponent> (Arrays.asList(
new TextFile("TextFile1"), // 1
new TextFile("TextFile2"), // 1
new TextFile("TextFile3")))), // 1
new Directory("directory",
new ArrayList <FileComponent> (Arrays.asList(
new TextFile("TextFile1"), // 1
new TextFile("TextFile2"), // 1
new Directory("directory",
new ArrayList <FileComponent> (Arrays.asList(
new TextFile("TextFile1"), // 1
new VideoFile("VideoFile1"), // 3
new VideoFile("VideoFile2") // 3
))
)
))
)
))
);
System.out.println("Total files: " + root.getTotalSize()); // 12
}
}

Diagram

Loading graph...

Some other examples

  • File System: As mentioned earlier, a file system on a computer consists of files and directories. Each directory can contain several files and directories. Here, the Composite pattern is very useful to treat files and directories in the same way.
  • Graphics Rendering: In graphics editors, shapes can be simple (like a line or a circle) or complex (a combination of multiple shapes). The Composite pattern allows treating simple and complex shapes uniformly.
  • Organizational Structures: In an organization, an employee can be a manager or a general worker. A manager might have other managers or workers reporting to them. The Composite pattern allows treating all employees uniformly.
  • Menu Systems: In graphical applications, menus that contain menu items are common. Each menu item could be a simple action or a submenu. The Composite pattern allows treating all menu items uniformly.
  • HTML DOM: In a web page, an HTML element can contain other HTML elements. The Composite pattern allows treating individual and composite elements uniformly.
  • Networking: In a network, a node could be a terminal node or a composite node that contains other nodes. The Composite pattern allows treating all nodes uniformly.
  • Game Programming: In a game, a game object could be a simple entity (like a bullet) or a composite entity (like a vehicle). The Composite pattern allows treating all game objects uniformly.
  • GUI Widgets: In a graphical user interface, a widget could be a simple widget (like a button) or a composite widget (like a panel). The Composite pattern allows treating all widgets uniformly.
  • Geometric Shapes: In a vector graphics editor, a shape can be simple (like a circle) or complex (a combination of multiple shapes). The Composite pattern allows treating simple and complex shapes uniformly.
  • Product Assemblies: In manufacturing, a product could be a simple part or an assembly of multiple parts. The Composite pattern allows treating all products uniformly.

Recommended articles