Java features explained with examples

Using Composite Java pattern to switch the lights on

Design patterns is probably one of the most popular topics in the Java blogosphere. Yet many times the examples used to illustrate the different design patterns are not very engaging. To remedy that, this post explains the Composite pattern in a fun way.

Since there are already many descriptions of this pattern out there, we will not repeat it again. The only thing to remember is that the Composite pattern is used to apply a recursive action on a tree structure.

 

Composite Java pattern example

As we said, the whole point of this post is the example so let’s get on with it!

Imagine a building, perhaps a hotel, with several floors and each floor containing several rooms.

This hotel is very special though, because it does not have a master switch box. Therefore, whenever it is necessary to switch on/off the lights, someone has to go floor by floor and room by room flipping the corresponding switch.

In order to model the hotel, we will start by defining an interface with the basic operations to switch the lights on and off . The interface will be called ‘Component’ to use the terminology normally associated to the Composite pattern.

public interface Component {
   void switchLightsOn();
   void switchLightsOff();
}

 

Next, we define the classes to represent the hotel: Building, Floor and Room. Each class implements the interface according to these rules:

  • the Building is considered to have the lights on/off when all its floors are on/off
  • each Floor is considered to have the lights on/off when all its rooms are on/off
  • each Room represents internally its on/off state

With these rules in mind, here is the definition of the 3 classes:

 

Building

public class Building extends ArrayList<Floor> implements Component{

   @Override
   public void switchLightsOn() {
      for (Floor floor : this) {
         floor.switchLightsOn();
      }
   }

   @Override
   public void switchLightsOff() {
      for (Floor floor : this) {
         floor.switchLightsOff();
      }
   }
}

 

Floor

public class Floor extends ArrayList<Room> implements Component {
   
   private int floorNumber;
   
   public Floor(int floorNumber){
      this.floorNumber=floorNumber;
   }

   @Override
   public void switchLightsOn() {
      for (Room room : this) {
         room.switchLightsOn();
      }
   }

   @Override
   public void switchLightsOff() {
      for (Room room : this) {
         room.switchLightsOff();
      }
   }
}

 

Room

public class Room implements Component {

   private boolean lightsOn = false;
   private int roomNumber;
   
   public Room(int roomNumber){
      this.roomNumber=roomNumber;
   }
   
   @Override
   public void switchLightsOn() {
      lightsOn = true;
   }

   @Override
   public void switchLightsOff() {
      lightsOn = false;
   }

   public boolean isLightsOn() {
      return lightsOn;
   }
}

 

Just a few comments about the above definitions:

  • for convenience, the classes Building and Floor extend ArrayList to inherit its containment capability
  • Building forwards the calls to each of its floors and, similarly, Floor forwards the calls to each of its rooms. This “recursive forwarding” is a distinctive feature of the Composite pattern.
  • the overall effect is that any action on Building starts a chain of recursive operations that propagate through Floors and Rooms. This “domino effect” is also characteristic of the Composite pattern.

 

To see these ideas in action, let’s run a test. First, we need to create a building (by default, the lights are off). Then, after calling ‘switchLightsOn’ on the building, the lights of all rooms in the hotel switch on.

 

public class CompositeTest {

   private Building building;
   
   @Before
   public void createBuilding(){
      
      building = new Building();
      
      //1st floor
      Floor floor = new Floor(1);
      floor.add(new Room(11));
      floor.add(new Room(12));
      building.add(floor);
      
      //2nd floor
      floor = new Floor(2);
      floor.add(new Room(21));
      floor.add(new Room(22));
      floor.add(new Room(23));
      building.add(floor);
      
      //3rd floor
      floor = new Floor(3);
      floor.add(new Room(31));
      floor.add(new Room(32));
      floor.add(new Room(33));
      building.add(floor);
      
   }

   
   @Test
   public void buildingLightsAreOn(){

       //checking that all rooms are off
      for(Floor floor : building){
         for(Room room : floor){
            assertEquals(false,room.isLightsOn());
         }
      }

      building.switchLightsOn();

       //checking that all rooms are on
      for(Floor floor : building){
         for(Room room : floor){
            assertEquals(true,room.isLightsOn());
         }
      }
   }
}

 

I hope you have enjoyed this example. If you want to give it a go yourself, here’s the link to the source code of the Composite pattern example.

29787total visits,15visits today

No Comments Yet

Leave a Reply

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