Design Pattern | Composite Pattern

What Is the Composite Pattern?

  • The word composite means mixed or compound.
  • The Composite pattern creates a recursive structure by treating containers and contents uniformly.
  • To treat directories and files together as directory entries, it can be useful to treat containers and contents as the same kind of thing. For example, a container may contain contents, and it may also contain another container. In this way, a recursive structure can be created.
  • In GoF design patterns, it is classified as a structural design pattern.

Composite Pattern Example Program

This example program displays a list of directories and files.

Class Diagram
Composite Pattern Class Diagram

1. Entry Class

This is the base class for File and Directory.

Entry.java

package com.devkuma.designpattern.structural.composite;

public abstract class Entry {

    public abstract String getName();

    protected abstract void printList(String prefix);

    public void printList() {
        printList("");
    }
}

2. File Class

This class represents a file.

File.java

package com.devkuma.designpattern.structural.composite;

public class File extends Entry {

    private String name;

    public File(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + name);
    }
}

3. Directory Class

This class represents a directory.

Directory.java

package com.devkuma.designpattern.structural.composite;

import java.util.ArrayList;
import java.util.Iterator;

public class Directory extends Entry {

    private String name;
    private ArrayList<Entry> directory = new ArrayList();

    public Directory(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    public Entry add(Entry entry) {
        directory.add(entry);
        return this;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + name);
        Iterator<Entry> it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = it.next();
            entry.printList(prefix + "/" + name);
        }
    }
}

4. Main Class

This is the class that executes the main processing.

Main.java

package com.devkuma.designpattern.structural.composite;

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

        Directory workspaceDir = new Directory("workspace");
        Directory compositeDir = new Directory("composite");
        Directory testDir1 = new Directory("test1");
        Directory testDir2 = new Directory("test2");
        workspaceDir.add(compositeDir);
        workspaceDir.add(testDir1);
        workspaceDir.add(testDir2);

        File directory = new File("Directory.java");
        File entity = new File("Entity.java");
        File file = new File("file.java");
        File main = new File("main.java");
        compositeDir.add(directory);
        compositeDir.add(entity);
        compositeDir.add(file);
        compositeDir.add(main);
        workspaceDir.printList();
    }
}

5. Execution Result

/workspace
/workspace/composite
/workspace/composite/Directory.java
/workspace/composite/Entity.java
/workspace/composite/file.java
/workspace/composite/main.java
/workspace/test1
/workspace/test2

Advantages

Because all objects(File and Directory) share a common abstract class, the client does not need to be aware of whether something is a File or a Directory and can treat them uniformly.
Also, even if a new class such as SymbolicLink is added, client processing is not affected as long as the interface of the base class(Entry) does not change.