GOF: Composite Pattern

Composite Pattern, in a way, literally takes of from where Decorator Pattern ends. One might be inclined to think of it as a specialized case of Decorator Pattern which composes one or more instances of similiar objects and manipulate them uniformly. This allows the client to work on a single entity or composition of entity uniformily, or in other words, it eliminates the need of programmers to discriminate between leaf and branch, and thereby avoid writing specific logic to handle both scenarios.

The key components of Composite Pattern can be defined as follows

Component
Components defines the interface that specifies the behavior that needs to be uniformly executed by primitive object or composite object.

Leaf
The non-decomposible leaf object, which defines the behavior for the primitive object

Composite
Holds the collection of Child components, and implements the behavior of components that have children.

That would be enough of theory, let’s write some code now. For the sake of example, we will write a little program that would print the size of given File/Directory. Let’s begin by defining the Component interface.

public interface IMetaInfo
{
double GetSize();
}

The next step would be define the behavior of the primitive object, which in this case would be the File.

public class FileObject : IMetaInfo
{
private string _fileName;
public FileObject(string fileName)
{
_fileName = fileName;
}
public double GetSize()
{
Console.WriteLine($"Calculating size of {_fileName}");
return 0;
}
}

As one can understand, the actual behavior is different for File and Directory. The size of Directory needs to be cumiliative size of all the individual Files in it. Let’s define the behavior for Directory Class now.

public class DirectoryObject : IMetaInfo
{
private string _directoryName;
private List<IMetaInfo> _fileCollection;
public DirectoryObject(string directoryName)
{
_directoryName = directoryName;
_fileCollection = new List<IMetaInfo>();
}

public void Add(IMetaInfo file)
{
_fileCollection.Add(file);
}

public void Remove(IMetaInfo file)
{
_fileCollection.Remove(file);
}
public double GetSize()
{
var directorySize = 0d;
foreach (var file in _fileCollection)
{
directorySize += file.GetSize();
}
return directorySize;
}
}

As one could see in the example code, the Directory Structure uses the bahavior of the individual File Structure to calculate the cumiliative Size. Let’s write the code for Client now and see how client can work without having the knowledge of what exactly it is dealing with.

class Client
{
static void Main(string[] args)
{
Console.WriteLine("When using Composite Object");
var directoryInstance = new DirectoryObject("BaseDirectyory");
for(var i = 0; i < 10; i++)
{
directoryInstance.Add(new FileObject($"{i.ToString()}_File"));
}

GetSize(directoryInstance);

Console.WriteLine("When using Component Object");
var fileInstance = new FileObject("Another File Instance");
GetSize(fileInstance);
Console.ReadLine();
}

private static void GetSize(IMetaInfo instance)
{
instance.GetSize();
}
}

The Client.GetSize() method accepts an instance of IMetaInfo as the parameter. It doesn’t really know whether it is actually dealing with a Composite object or a Primitive one.

The Composite pattern is a highly useful pattern when one needs to deal with a irregular structure of objects and composite of objects, enabling the client to deal with them uniformly.

The entire source code shown in this example are available in my GitHub.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s