Composite Design Pattern Java Code Case Study

Composite Design Pattern

Composite Design Pattern is a Structural Design Pattern that comes in the picture when we have a part-whole relationship. Part-whole relationship means multiple objects create one whole object.

When we have to treat all these objects in a similar way or whenever we deal with the tree structure of objects, we use the Composite Design Pattern.

The intent of a composite design pattern is to “compose” objects into tree structures to represent part-whole hierarchies. It allows us to have a tree structure and ask each node in the tree structure to perform a task.

Composition Design Pattern is not exactly similar to the composition principle but an enhancement of that principle.

When to use a Composite Design Pattern?

When we want to compose objects into tree structures to represent part-whole hierarchies.

When we want to treat individual objects and compositions of objects uniformly.

 

Composite Design Pattern Implementation

Create an abstract class or interface as the component that declares all the methods applicable to both leaf and composite.

Create a class as Leaf that will extend to component. The leaf class will not have any child.

Create a composite class that extends to the component. The class will store child components and will provide an implementation for addition, removal methods.

Composite Design Pattern Real World Example

Consider our client is a gaming company that has many games under it of the different genres.

The client is looking for a way to display all the games title they have maintaining the hierarchy.

Component Class

Games will act as the component. We will declare all the methods applicable for leaf and composite.

package com.adevguide.java.designpatterns.composite;

/**
 * @author PraBhu
 *
 */
// Component class
public abstract class Games {

    private String name;

    public Games(String name) {
        this.name = name;

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public abstract void listGames();

    public abstract void addGame(Games game);

    public abstract void removeGame(Games game);

}

Games(String name) is a constructor that accepts only the name of the game/genre.

Leaf Class

GameTitle will act as Leaf Class. This will extend to Games Class (Component). This class will have Leaf implementation for all the methods defined in the Games Class.

package com.adevguide.java.designpatterns.composite;

/**
 * @author PraBhu
 *
 */

// leaf class
public class GameTitle extends Games {

    private int price;

    public GameTitle(String name, int price) {
        super(name);
        this.price = price;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public void listGames() {
        System.out.println(getName() + " is available for " + getPrice() + "$");
    }

    @Override
    public void addGame(Games game) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void removeGame(Games game) {
        throw new UnsupportedOperationException();

    }

}

public GameTitle(String name, int price) is a constructor that will accept the name and price of the Game Title.

listGames() is a leaf level implementation of the Game class.

addGame(Games game) and removeGame(Games game) are not applicable on leaf level so we will implement UnsupportedOperationException.

How is Java Pass by Value and Not by Reference [4 Examples]

Composite Class

GameGenre will act as the composite class. This will extend to the Game Class(Composite) and will have a composite implementation of all the methods.

package com.adevguide.java.designpatterns.composite;

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

/**
 * @author PraBhu
 *
 */
// Composite Class
public class GameGenre extends Games {

    private List<Games> gameList = new ArrayList<>();

    public GameGenre(String name) {
        super(name);
    }

    @Override
    public void listGames() {
        System.out.println(getName());
        gameList.forEach(Games::listGames);
    }

    public void addGame(Games game) {
        gameList.add(game);
    }

    public void removeGame(Games game) {
        gameList.remove(game);
    }


}

List<Games> gameList is an array that will store all the genre and game within it.

listGames() has the implementation to iterate and print all nodes present in gameList.

Client Class

Client will act as the client class. This class will have the logic to show the functionality of Composite Design Pattern.

package com.adevguide.java.designpatterns.composite;

/**
 * @author PraBhu
 *
 */
public class Client {

    public static void main(String[] args) {

        final String SEPERATOR = "***************************";
        Games gameType = new GameGenre("PC Games");

        gameType.addGame(createMiscGame());
        gameType.listGames();
        System.out.println(SEPERATOR);

        gameType.addGame(createSportGames());
        gameType.listGames();
        System.out.println(SEPERATOR);

        gameType.addGame(createRacingGames());
        gameType.listGames();

        System.out.println(SEPERATOR);

    }

    private static Games createSportGames() {

        Games sportGames = new GameGenre("Sport Games");

        Games fifa = new GameTitle("FIFA 19", 10);
        Games nba = new GameTitle("NBA 2K19", 6);
        sportGames.addGame(fifa);
        sportGames.addGame(nba);
        return sportGames;

    }

    private static Games createRacingGames() {

        Games racingGames = new GameGenre("Racing Games");

        Games nfs = new GameTitle("Need For Speed", 15);
        Games realRacing = new GameTitle("Real Racing", 5);
        racingGames.addGame(nfs);
        racingGames.addGame(realRacing);
        return racingGames;

    }

    private static Games createMiscGame() {
        Games sims = new GameTitle("Sims 3", 1);
        return sims;
    }

}
  1. createSportGames() is a method that has a return type of Games. This method will create two sport game leaf objects and then attach it to a node object. This will return a genre.
  2. createRacingGames()  is a method that has a return type of Games. This method will create two racing game leaf objects and then attach it to a node object. This will return a genre.
  3. createMiscGame() is a method that has a return type of Games. This will directly return the Leaf object instead of the node object ().

OUTPUT:

PC Games
Sims 3 is available for 1$
***************************
PC Games
Sims 3 is available for 1$
Sport Games
FIFA 19 is available for 10$
NBA 2K19 is available for 6$
***************************
PC Games
Sims 3 is available for 1$
Sport Games
FIFA 19 is available for 10$
NBA 2K19 is available for 6$
Racing Games
Need For Speed is available for 15$
Real Racing is available for 5$
***************************

 

Composite Design Pattern Advantages

  • Less number of objects reduces the memory usage, and it manages to keep us away from errors related to memory like java.lang.OutOfMemoryError.
  • Whenever a tree like structure is required or group of object is should behave as a single object, We should use Composite Design Pattern.
  • Although creating an object in Java is really fast, we can still reduce the execution time of our program by sharing objects.

Composite Design Pattern Pitfalls

  • It is difficult to restrict what we add to the hierarchy. If multiple types of nodes are present in the system, the client code ends up doing a runtime check to ensure that the operation is available in the node. There might be some methods specific to composite or leaf.
  • Creating the original hierarchy can still be a complex implementation, especially when we are using caching to reuse nodes and the number of nodes is high. This is because the composite design pattern tells us the structure of the tree; it doesn’t tell how to build the tree.

Composite Design Pattern Example

This pattern is used extensively in UI frameworks.

java.awt.Container#add(Component) is a great example of a composite pattern in Java and used a lot in Swing framework.

 

Composite Design Pattern Source Code

The source code from this tutorial is available in our GitHub Repository.

GitHub Source Code

References

https://www.oodesign.com/composite-pattern.html