PHP Patterns: The Composite Pattern

A while ago, I covered The Observer Pattern, a really useful pattern, particularly when it comes to event-handling systems.

Today, I’m going to take a look at the Composite Pattern, another really useful pattern that allows you to treat individual objects and collections of those objects as if they were the same. That may sound a little strange, but we’ll look at a concrete example, which should help clarify things: a List-based system.

A List-Based System

Let’s consider we want to design a system that manages lists. Typically, a list consists of one or more items. To make it more concrete, imagine your shopping list:

  • Milk
  • Eggs
  • Bread

That’s a very simple list with three items. So far, so good.

But lists aren’t always so simple. For example, to-do lists often include other lists, and even other unrelated actions. You need a list of errands you need to do, such as posting a letter, returning library books, and getting a new book. Suddenly, our to-do list is a lot more complicated.

  • Grocery Shopping List
    • Milk
    • Eggs
    • Bread
  • Go to library
    • Return Craig Sefton’s book
    • Get another PHP book
  • Post letter at post office

As you can see, our list now has sub-lists, as well as a single list item for posting the letter. You’ll notice as well that they’re all very similar in that they basically consist of a description. You could also imagine each list and item having meta-data like “date created”, “date completed” and so on.

So, ideally, if we would want to come up with a way of doing this in PHP (or any other language), individual list items as well as groups of list items should share similar characteristics to each other.

We should also have the ability to add or remove individual items and collections to and from other lists.

Lastly, we would want to have the ability of being able to create very specific types of lists (i.e. Grocery Shopping List, Film list, Music list … whatever you need).

This may sound difficult, but it’s very easy to do when we use PHP’s abstract classes to outline a few core classes that we can then extend to build various lists and list items.

If you don’t know what an abstract class is, I suggest reading the PHP help files, but to give you a basic idea, an abstract class is like a blueprint class. By using the extends keyword, PHP allows you to take that blueprint, and apply it to new classes, thus forcing those classes to have the same functionality. You’ll see what I mean as we take a look at the code.

The Code

ListItem

Obviously, the first thing we want to outline is what an individual list item is.

We’ll keep things simple, so all we want is an object that has a description, date created, date the item is due to be completed, and date that the item was completed. Here’s the code.

	abstract class ListItem {
		
		protected $description = "";
		protected $datedue = null;
		protected $datecreated = null;
		protected $datecompleted = null;
		
		function __construct( $description, $datedue=null ) {
			
			$this->setDescription( $description );
			$this->setDateDue( $datedue );
			$this->setDateCreated( time() );
			
		}
		
		function getComposite() {
			return null;
		}
		
		function setDescription( $description ) {
			$this->description = $description;
		}
		
		function getDescription() {
			return $this->description;
		}
		
		function setDateDue( $datedue ) {
			$this->datedue = $datedue ;
		}
		
		function getDateDue() {
			return $this->datedue;
		}
		
		function setDateCompleted( $datecompleted ) {
			$this->datecompleted = $datecompleted;
		}
		
		function getDateCompleted() {
			return $this->datecompleted;
		}
		
		function setDateCreated( $datecreated ) {
			$this->datecreated = $datecreated;
		}
		
		function getDateCreated() {
			return $this->datecreated;
		}
		
	}

This is a very straightforward class, with a few getter and setter methods.

However, you likely would have noticed the function getComposite(), and all it does is return null. This method will become clear as we move onto the second abstract class, the CompositeListItem.

CompositeListItem

Now, we want CompositeListItem to handle those cases when we’re dealing with a collection of objects, rather than just an individual object. This means we’ll need the ability to add and remove list items as necessary.

First, let’s give you the code, and then I will explain what’s going on.

	abstract class CompositeListItem extends ListItem {
		
		protected $listitems = array();
		
		function getComposite() {
			return $this;
		}
		
		protected function listitems() {
			return $this->listitems;
		}
		
		function removeListItem( ListItem $listitem ) {
			
			$listitems = array();
		
			foreach ( $this->listitems as $thisitem ) {
				if ( $listitem !== $thisitem ) {
					$listitems[] = $thisitem;
				}
			}
		
			$this->listitems = $listitems;
			
		}
		
		function addListItem( ListItem $listitem ) {
		
			if ( in_array( $listitem, $this->listitems, true ) ) {
				return;
			}
		
			$this->listitems[] = $listitem;
			
		}
		
	}

The first thing to notice is that CompositeListItem is an abstract class, but it extends ListItem, thus inheriting all the characteristics of that abstract class. So, immediately, we’re setting the condition that collections and individual items have the same base characteristics.

CompositeListItem also contains the necessary functions we need to add and remove list items (addListItem() and removeListItem()), as necessary, as well as a simple array storage container $listitems.

But notice the getComposite() method again. Instead of returning null as with the abstract ListItem class, it returns itself. Why, and more importantly, what is this function used for?

The answer has to do with the fact that we need some method of telling us whether or not we’re dealing with a list item, or a collection of list items.

Since we’re treating collections and individual items as the same, but collections have the added abilities of being able to add and remove list items, we need to make sure that there is a way for programmers to know whether or not they are able to access this added functionality available to composites. getComposite() is a simple way of doing this: if it returns null, we’re dealing with a ListItem, if it’s an object, we’re dealing with a CompositeListItem.

Putting it Together

So, now that we have the base classes set out, let’s start to build our objects. Remember, we wanted a To Do List, Grocery List, a Library List, and a Post Office item. So let’s create these objects:

	class ToDoList extends CompositeListItem { }
	class GroceryList extends CompositeListItem { }
	class LibraryList extends CompositeListItem { }
	class GroceryItem extends ListItem { }
	class LibraryItem extends ListItem { }
	class PostOfficeItem extends ListItem { }

And that’s it! Now we can show the real magic when we start to use them:

	// Create our post office item
	$poItem = new PostOfficeItem( "Post letter at post office." );
	
	// Create our Library List and Library Items
	$libList = new LibraryList( "Go to the Library" );
	$libItem1 = new LibraryItem( "Return Craig Sefton's book." );
	$libItem2 = new LibraryItem( "Get another PHP book." );
	$libList->addListItem( $libItem1 );
	$libList->addListItem( $libItem2 );
	
	// Create our Grocery List and Grocery Items
	$groceryList = new GroceryList( "Grocery Shopping List" );
	$groceryItem1 = new GroceryItem( "Milk" );
	$groceryItem2 = new GroceryItem( "Eggs" );
	$groceryItem3 = new GroceryItem( "Bread" );
	$groceryList->addListItem( $groceryItem1 );
	$groceryList->addListItem( $groceryItem2 );
	$groceryList->addListItem( $groceryItem3 );

	// Create our ToDo List, and add our other lists and items to it	
	$todoList = new ToDoList( "My ToDo List" );
	$todoList->addListItem( $groceryList );
	$todoList->addListItem( $libList );
	$todoList->addListItem( $poItem );

The power of such a design quickly becomes apparent when you realise that you can introduce operations that iterate across all CompositeListItem objects or ListItem objects held in a collection:

	foreach ( $todoList->listitems() as $todo ) {
		
		$list = $todo->getComposite();
		if ( is_object( $list ) ) {
			// we've got a composite list item
		} else {
			// we've got an individual list item
		}
	
	}

That’s just the basics, but hopefully you can already see how it could be extremely powerful. I’m sure you can figure out loads of other ways it could be made useful. Enjoy!

This entry was posted in PHP, Programming and tagged , , , , .

One Response to “PHP Patterns: The Composite Pattern”

  1. Fab says:

    This is nice and clever BUT doesn’t really illustrate the use of composition.

    The point of composition is to delegate some responsibility to another strategy object that itself is abstract and has subclasses that contain the real specific logic. The superclass is simplified as it only deals with the strategy object, it will receive the strategy object as a parameter and invoke its method(s). New strategies can then be added just by extending the strategy object and that requires to changes to the superclass.

    Without the composition you would be creating a deep three of superclasses and subclasses whenever you need to add new specific logic.

    It’s about making the code more flexible without making changes to your superclass

Leave a Reply