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.
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.
Examples
Here are some examples of composite pattern in action.
File system
In file system we can see different types of items that can be generalized to something called file system item. There are files, soft links and hard links. As a composite there is file system folder. It can contain files and links both.
Of course, we can extend this model to support also network shares and external locations like FTP sites, Google Drive, SharePoint document libraries and OneDrive.
Media library
One related example is media library where we organize our photos and videos to folders. There is also at least one composite component – media folder.
There is more room to play with composites as folder is not the only composite we may have. There are also albums and video collections published originally as CD-s and DVD-s. It’s the decision of technical design if we need more composites besides media folder or not.
Tasks
There are many systems that help us to organize and monitor our work. Typically we see entities like tasks and projects in these systems.
The model shown here is controversial as we can also make task implementations to be composites and also we can leave out project. Yes, we can leave out project and handle first level tasks in tasks hierarchies as projects. In this case we have components and composites in same classes and applying composite pattern turns pointless as problems it solves are already solved.
Parties
As a more complex example let’s see implementation of Party generalization from The Data Model Resource Book by Lev Silverston. The Data Model Resource Books are the must reading for all those developers who work on classes design and data modeling.
My blog post about parties generalization gives basic ideas about implementing parties. The model here is example about how to model more complex cases where relationships between parties are needed and we still need a way how to handle all parties as one.
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.
View Comments (0)