Java programming language logo

Composite Design Pattern

Composite pattern enables to manage groups of objects and single objects in the same way. It organize the objects into a tree structure and handle the whole tree as it were a single object.

A component is an object in a tree structure. A component may contain other composite or not. If not, it is a leaf node.

This pattern is used when objects and compositions of objects need to be handled uniformly and can be organized into a tree structure. To achieve this, the objects and the compositions of objects have to implement the same interface.

Composite pattern is a structural pattern.

Example

In the next Java example, we are handling beverages. The beverages come in bottles, and of course they are drinkable. A single bottle can be sold and consumed in the same way like a pack of bottles. A bottle water is drinkable, and a pack of waters are also. So there should be a manner to handle them in the same way.

If something drinkable, it should implement the Drinkable interface, and by that gets a consume method.


package com.programcodex.designpatterns.composite;

public interface Drinkable {

    void consume();
}

Next, we create an implementation for the Drinkable interface. That will be the Bottle. When it is instantiated, it gets its content that is the type or name of the drink.


package com.programcodex.designpatterns.composite;

public class Bottle implements Drinkable {

    private String content;

    public Bottle(String content) {
        this.content = content;
    }

    @Override
    public void consume() {
        System.out.format("A %s is getting consumed.%n", content);
    }
}

The bottles can be packed together. The BottlePack represent this. It has a collection that holds the bottles, which are drinkables. The whole pack is drinkable, so it also implements the Drinkable interface.


package com.programcodex.designpatterns.composite;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BottlePack implements Drinkable {

    private List<Drinkable> beverages = new ArrayList<>();
    
    public void add(Drinkable... drinkables) {
        beverages.addAll(Arrays.asList(drinkables));
    }
    
    @Override
    public void consume() {
        beverages.forEach(Drinkable::consume);
    }
}

And we are done. It’s time to run these things.

In the test class, a few bottle of beers are created and packed. Also two bottle of wines and a pack of waters are created. These are all packed together into a big pack. After that, another bottle is created: a single cola. The big pack and the single cola are Drinkable, so they can be sold a consumed in the same way. That means, both of them are accepted by the sell method.


package com.programcodex.designpatterns.composite;

public class TestComposite {

    public static void main(String[] args) {
        Drinkable beer1 = new Bottle("Lager Beer");
        Drinkable beer2 = new Bottle("Light Beer");
        Drinkable beer3 = new Bottle("Non-Alcoholic Beer");
        
        BottlePack packOfBeer = new BottlePack();
        packOfBeer.add(beer1, beer2, beer3);
        
        Drinkable redWine = new Bottle("Red Wine");
        Drinkable whiteWine = new Bottle("White Wine");
        
        Drinkable water1 = new Bottle("Water");
        Drinkable water2 = new Bottle("Water");
        Drinkable water3 = new Bottle("Water");
        
        BottlePack packOfWater = new BottlePack();
        packOfWater.add(water1, water2, water3);
        
        BottlePack bigPack = new BottlePack();
        bigPack.add(packOfBeer, redWine, whiteWine, packOfWater);

        System.out.println("A big pack of beverages got sold: ");
        sell(bigPack);
        
        System.out.println(
                "\nA single bottle also got sold in the same manner:");
        Drinkable cola = new Bottle("Cola");
        sell(cola);
    }
    
    private static void sell(Drinkable drink) {
        drink.consume();
    }
}

The output of the above test class:

The Composite Design Pattern