Composite Pattern

Composite pattern is used in many applications. It helps to build hierarchies of similar objects and handle slices of hierarchy as one. Maybe one of the most popular implementation of this pattern in .NET world is Nodes collection of ASP.NET TreeView control. In this posting I will introduce you composite pattern and provide some implementation tips and samples about it.

Definition

Composite pattern compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
p. 233, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides.

Structure of composite class

Here is the structure of composite pattern. Component class is the one that client classes use. They know how to instantiate this class and they don’t have any reason to know more about what’s going on inside it.

Composite pattern

There are two types of subclasses – leaf and composite classes. Leaves are classes that have no children. Composites are classes that support also children and all operations on composite classes are also performed on their children.

NB! Leaf and composite on the class diagram above are illustrative, In practice we have one base class or interface that defines the public interface of composite and that other classes inherit.

Implementing composite pattern

Before going to write any implementations of composite pattern we need the base class or interface that implementation classes can use. We will write simple skeleton for media library that supports music and video files and media folders structure.

We start with defining base class.


public abstract class MediaItem
{
   
public virtual string Title { get; protected set
; }

   
public abstract void
Play();
   
public abstract void Add(MediaItem
item);
   
public abstract void Remove(MediaItem
item);
   
public abstract int
Count();
   
public abstract bool IsComposite { get; }

What is IsComposite? Composite pattern has one problem – how should leaf classes that doesn’t have internal collection handle Add(), Remove() and Count? Should they ignore these calls or should they throw exception? If they should throw exception then how to check the fault situation? Checking type is not good practice and refers usually to design problems. IsComposite property helps us to solve the problem. Leaf classes return false and composite classes return true.

Let’s implement first media folder. Media folder can contain other media folders and media items that represents songs and videos. Notice how Play() method is implemented – no file is directly hosted or played by media folder. It only delegates the method call to the collection of child nodes.


public class MediaFolder : MediaItem
{
   
private readonly ICollection<MediaItem
> _items;

   
public
MediaFolder(string title)
    {
Title = title;
        _items =
new Collection<MediaItem
>();
    }

   
public override void
Play()
    {
       
foreach (var item in
_items)
            item.Play();
    }

   
public override void Add(MediaItem
item)
    {
        _items.Add(item);
    }

   
public override void Remove(MediaItem
item)
    {
        _items.Remove(item);
    }

   
public override int
Count()
    {
       
return
_items.Count;
    }

   
public override bool
IsComposite
    {
       
get { return true; }
    }
}

Now let’s define media item class for sound files. Notice how collection related methods all throw exception when called and IsComposite property returns false. Also notice how Title of media item is handled differently now.


public class MusicItem : MediaItem
{
   
private readonly string
_path;

   
public MusicItem(string
path)
    {
        _path = path;
        Title =
MusicFile
.Title(path);
    }

   
public override void
Play()
    {
       
// load file and play it
    }

   
public override void Add(MediaItem
item)
    {
       
throw new NotImplementedException
();
    }

   
public override void Remove(MediaItem
item)
    {
       
throw new NotImplementedException
();
    }

   
public override int
Count()
    {
       
throw new NotImplementedException
();
    }

   
public override bool
IsComposite
    {
       
get { return false; }
    }
}

Same way we can add support for other media files like videos, photos and presentations. Maybe we can also write media item class for online TV or other live streams.

Main benefits

Although composite pattern can be tricky to implement and you may find many obstacles on your way when building something complex, there are still some very good reasons why to use this pattern:

  • for client code it doesn’t matter what kind of composite object it is using, leaf and composites both have treated as same,
  • it is easy to add new types of components without touching any client code,
  • the design of your application gets more general.

Wrapping up

Composite pattern lets you define hierarchies of similar objects that share same base class or interface. There are two types of classes. Leaf classes do the actual work and they don’t have child nodes. Composite classes have child nodes and they just delegate their operations to child nodes. Composite pattern makes your code more general and avoids dependencies that may otherwise turn your code more complex and harder to manage.


One thought on “Composite Pattern

Leave a Reply

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