Your Guide to Design Patterns: Composite Pattern

Design patterns - composite design pattern

This week we’ll continue our exploration of design patterns and look at a commonly used structural pattern, the Composite design pattern.

The Composite Pattern

The Gang of Four book defines the Composite pattern as follows:

“Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.”

We can apply the Composite pattern when there is a part-whole hierarchy of objects, and a client needs to deal with objects uniformly regardless of the fact that an object might be a leaf (simple object) or a branch (composite object).

The key to the Composite pattern is an abstract class that represents both the primitive objects and their containers.

This simplifies the client code. The client treats simple objects (the leaves) and composite objects (the branches) in the same way. The client doesn’t know or care whether they’re dealing with one or the other. It also makes it very easy to add new kinds of components or containers. They will automatically work with the existing code.

For example, think about working with a file system. Files are simple objects. Directories (folders) are compositions of files and other directories. Both files and directories have names, sizes, attributes, etc. It would be very convenient to treat both files and directories in a uniform way. We could define a FileResource abstract class that would model the similarities between files and directories. The standard java.io.File class springs to mind for that.

Another common example comes from the manufacturing industry. Let’s say we were building engines. Engines are built from individual parts and various assemblies. An assembly is a group of parts that is pre-built (i.e., assembled). Assemblies are built from individual parts and maybe other assemblies. The Composite pattern allows us to treat both assemblies and individual parts as if they were the same type. We can then programmatically process them in the same way, which simplifies our code.

Structure

The Composite pattern consists of the following participants.

  • Base Component – the base Component is the interface for all objects in the composition. It can be an interface or an abstract class with the methods common to all the objects.
  • Leaf – Defines the behaviour for the elements in the composition. It is the building block for the composition and implements base component. It doesn’t contain references to other Components.
  • Composite – It implements the operations in the base Component and contains references to other Components.
  • Client – manipulates objects in the composition through the base Component interface.

Composite in the Java APIs

The Composite pattern is recognisable by structural methods taking an instance of the same interface/abstract type and placing it in a tree structure. Some examples from the standard Java APIs:

  • The Component and Container classes in the java.awt package. A Container extends the abstract superclass of Component. A Container can contain other Components. We can add()remove() and otherwise manipulate Components. This gives rise to a tree structure of Components.
  • The JSF (JavaServer Faces) javax.faces.component.UIComponent can contain other UIComponents. This is another tree structure.

Example

In our first person shooter game, we’ve been coming up with all sorts of great ideas to enhance game play, and maybe even spin off some other games while we’re about it.

Our next brilliant idea is creating mechanised fighting units, like self-driving armoured vehicles and autonomous fighting robots. We’d obviously want to command them and aim them towards specific targets. We’d like to command the robots in the same way as we command the tanks. We’d also like to command the robots in the same way if they were moving under their own power or riding in an self-driving armoured fighting vehicle.

We could model these robots and vehicles as MechanisedFightingVehicle and MechanisedFightingRobots.

We know that all fighting units should fight, so in a time-honoured practice we will create an interface to contain the common fight() method. To make it more generic to our game, we will just call it a FightingUnit:

public interface FightingUnit {
   public void fight();
}

Both the MechanisedFightingVehicle and MechanisedFightingRobot classes will implement this interface. We see that some MechanisedFightingUnits would contain other MechanisedFightingUnits. A MechanisedFightingVehicle would contain MechanisedFightingRobots. This containment relationship can be modelled as a common abstract base class containing a collection of MechanisedFightingUnits with methods to add and remove units.

The following code is the base class for the containment relationship.

public abstract class MechanisedFightingUnit implements FightingUnit{

   private String name;

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

   public String getName() {
              return name;
   }

   // abstract methods to be implemented appropriately by subclasses
   public abstract void fight();

      public abstract void add(MechanisedFightingUnit unit);
      public abstract void remove(MechanisedFightingUnit unit);
      public abstract MechanisedFightingUnit[] getUnits();

} // end of class

We will leave the actual collection implementations to the container classes which could be MechanisedFightingVehicleMechanisedFightingVehicleTransporterMechanisedFightingBattleship, etc.

import java.util.*;

public class MechanisedFightingVehicle extends MechanisedFightingUnit {

   private List units;

   public MechanisedFightingVehicle(String name) {
      super (name);
      units = new ArrayList<>();
   }
   public void add(MechanisedFightingUnit unit) {
      units.add(unit);
   }
   public void remove(MechanisedFightingUnit unit) {
      units.remove(unit);
   }
   public MechanisedFightingUnit[] getUnits() {
      return units.toArray(new MechanisedFightingUnit[units.size()]);
   }

   public void fight() {
      // appropriate code for a self-driving vehicle to fight
      System.out.println("Vehicle " + getName() + " fighting!");

      // as well as a call for all contained FightingUnits to fight
      for (MechanisedFightingUnit unit : getUnits())
         unit.fight();
   }
} // end of class

The robots would not contain any other fighting units, so the collection code – the add()remove() and getUnits() methods – would be implemented as empty (or close to empty) methods:

public class MechanisedFightingRobot extends MechanisedFightingUnit {

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

   // container methods implemented as empty/near-empty methods
   public void add(MechanisedFightingUnit unit) {}
   public void remove(MechanisedFightingUnit unit) {}
   public MechanisedFightingUnit[] getUnits() {
          return new MechanisedFightingUnit[0];
   }

   public void fight() {
      // appropriate code for a single robot to fight
      System.out.println("Robot " + getName() + " is fighting!");
   }
} // end of class

These classes would be used in client code as follows:

MechanisedFightingUnit t800   = new MechanisedFightingRobot("T-800");
MechanisedFightingUnit t900   = new MechanisedFightingRobot("T-900");
MechanisedFightingUnit t1000  = new MechanisedFightingRobot("T-1000");
MechanisedFightingUnit botvee = new MechanisedFightingVehicle("BotVee");

// T-900 and T-1000 ride in a vehicle
botvee.add(t900);
botvee.add(t1000);

t800.fight();   // fights by itself
botvee.fight(); // fights together with the contained units

Comparison with Other Design Patterns

  • Composite and Decorator have similar structures. They both rely on recursive composition to organise an open-ended number of objects.
  • Decorator add responsibilities to objects without subclassing. Composite is focused on the representation of the tree structure, and not on embellishing the objects in the tree.
  • A Decorator can be used to decorate the base Component class with the add()remove()getComponent(), etc. methods to make it a Composite class.
  • An Iterator can be used to traverse through the composite tree structure.

What’s Next?

In the weeks ahead, we’ll continue examining some of the more useful design patterns. Stay tuned! And don’t forget to post a comment and let me know what you think.

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.