Composite Pattern Java

Vibhor Pareek
Javarevisited
Published in
3 min readSep 2, 2024

Introduction

Composite pattern is a structural design pattern aims at defining the structure of group of objects in a hierarchy. The pattern can be applied where you see a group of object sharing a tree like pattern or relationship between them like file->directories, employee in department which can further drill deep down and have child departments, mostly nodes in a tree structure.

The pattern is extensively used where :

  • Application need to aggregate data across the tree structure.
  • Client treats each object in an hierarchy uniformly.
  • Keep client contract simple & promotes uniformity.

Terminologies

  • Component : Interface for objects in the composition.
  • Leaf : Leaf object in composition with no children. Defines behavior via implementing component interface.
  • Composite : Object containing child component. Defining relation between components and defines behavior for components having children.

In this article, we will dig deep into an example possibly a problem of LLD design and solve it via composite pattern.

Problem

Lets say you need a design a music player where you can add songs to a playlist & play . To demonstrate the application of composite pattern, I am keeping a playlist and multiple songs added as part of LLD design.

Implementation

  1. Defining a base component interface as below.
public interface Playable {
void play();
}

2. Defining Leaf component which is song object in our case. Leaf object also defines a behavior which is play [Song to be played.]

public class Song implements Playable{

private final String title;
private final String artist;
private final String album;

public Song(String title,String artist,String album) {
this.title = title;
this.artist = artist;
this.album = album;
}

@Override
public void play() {
System.out.println("Playing song! : " + title + " " + artist + " " + album);
}
}

3. Defining composite object which is playlist in our case & containing songs to be played by the playlist. Below code has list of songs contained by composite object Playlist.

public class Playlist implements Playable{

private final String name;
private final List<Playable> songList;

public Playlist(String name) {
this.name = name;
this.songList = new ArrayList<>();
}

@Override
public void play() {
System.out.println("Playing the playlist : " + name);
songList.forEach(Playable::play);
}

public void add(Playable song) {
//Adding to the playlist
this.songList.add(song);
System.out.println("Added to the playlist ->" + name);
}

public void remove(Playable song) {
this.songList.remove(song);
}

}

4. Reaching here, we are almost done. Now we need to see the invoker in action as below.

public class Invoker {
public static void main(String [] args) {


Playable song1 = new Song("Song-A","Artist-A","Album-A");
Playable song2 = new Song("Song-B","Artist-B","Album-B");
Playable song3 = new Song("Song-C","Artist-C","Album-C");

Playlist playlist = new Playlist("Vibhor's Playlist");
playlist.add(song1);
playlist.add(song2);
playlist.add(song3);

playlist.play();

}
}

//Playing the playlist : Vibhor's Playlist
//Playing song! : Song-A Artist-A Album-A
// Playing song! : Song-B Artist-B Album-B
//Playing song! : Song-C Artist-C Album-C

4. Keeping it till here only, you can also add/extend Music Player class and invoke playlist in the class. Hope you got the the detailed explanation about composite pattern.

Conclusion

  1. The composite pattern structures the hierarchy consisting of individual and composite objects & can handle complex hierarchy as well.
  2. Clients treats basic and composite objects uniformly through a component interface which makes client code simple.
  3. Adding new components is easy and client can focus on aggregated functionality provided by composite object. This also promotes loose coupling in code & adheres to ‘O’ in SOLID principles.

--

--