Composite pattern drib design pattern series, part #2

Scroll this

What is design pattern

In software engineering, a software design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine code. It is a description or template for how to solve a problem that can be used in many different situations. Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system.

source: wikipedia

Composite pattern

Composite pattern describes how group of similar objects can be treated by other components of the system in the same way as single object.

For example in a drawing program we have to deal with various shapes, lines, ellipses, rectangles, etc … The program will manage them in a uniform way (draw, move, rotate, …). But also, we want to group various shapes  and manage the group in the same way as individual shapes.

The composite pattern describes a group of objects that is treated the same way as a single instance of the same type of object.

A single object is called  a leaf, a group of similar objects objects is called a composite. Other components of the system (for example drawing program) that are using the composite objects is called the client.

Example 1

Description

Implement vector drawing program with various shapes and a feature to group the shapes into groups.

draw/shape.php

All shapes have to implement methods draw(), move(), resize(). While methods add() and remove() will throw Exception. For individual shapes we will leave it like this, for groups we will override add() and remove() .

draw/rectangle.php

draw/circle.php

draw/shapegroup.php

ShapeGroup also extends Shape and implements same public methods as individual shape – so it can be used by the drawing program the same was as individual shapes. But it also implements add() and remove().

Test:

main.php

output:

The drawing program can draw individual shapes and the groups in the uniform way and it doesn’t need to know which objects are groups and which are individual shapes.

But, there is a  problem that needs to be solved: All classes in the pattern, the composite class ShapeGroup and individual shape classes ( Rectangle, Circle, …) should share the same interface.  The individual shapes can not implement add() and remove() – so they throw an Exception. This is not the best solution since drawing program might unexpectedly trigger  an error if it calls add() or remove() on object which is not composite.

This will throw exception:

main2.php

Output:

So, the client needs to be aware which objects are the individual shapes and which are groups of the shapes:

main3.php

Using instanceof is considered bad OO programming practice and it is best avoided.

Example 2

Lets introduce new method for the abstract class Shape, getComposite() which will return NULL for single object (leaf) and for composite object it will return its reference.

draw/shape.php

draw/shapegroup.php

main3.php

In the above example responsibility which composite object to create is left to the client. In some situation this responsibility can be transferred to the leaf object it self, so getComposite() will create new composite object and add current object into the composite object, it will make code in the client more simple and straightforward.

Example 3

Change getComposite() in the abstract class Shape:

draw/shape.php

main3.php

The client code is now more simple – but it lost the possibility to choose what kind of composite object to create. In this example it is ok since we are having only one composite object ShapeGroup.

Client can be simplified even more, by moving functionality of getComposite() to add():

Example 4

The add() and remove() methods for individual shapes will be refactored so that they will create new GroupShape object, add both the current object and the new object to the group and return the newly created composite object. The Shape class now looks like this:

draw/shape.php

add() is straightforward. remove() might look weird, but this is how it works: if client tries to remove the shape from itself empty ShapeGroup is created and returned, otherwise the current object is returned. In both cases the object that is being removed from the group is not part of the group any more.
(Other classes remain unchanged.)

Test:

main3.php

Output:

As you can see from example above – drawing program now can treat composite object ShapeGroup in the same way as individual shapes. It doesn’t need to know which object are of type ShapeGroup even when a calling add() to remove().

Conclusion

With composite pattern it is possible to create single objects and composite objects that will be treated in the uniform way by the client.

Only exception are add/remove methods: If a client wants to decide composite objects  to create – it needs some way to tell apart single objects from composite objects. If this responsibility can be transferred to the individual object, then single objects and composite objects can be treated in uniform way even for add/remove methods.

Submit a comment