The intent of composite pattern is to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
- A part-whole hierarchy should be represented so that clients can treat part and whole objects uniformly.
- A part-whole hierarchy should be represented as tree structure.
- Define a unified
Component
interface for both part (Leaf
) objects and whole (Composite
) objects. - Individual
Leaf
objects implement theComponent
interface directly, andComposite
objects forward requests to their child components.
- Component (IFileSystemComponent)
- declares an interface for objects in the composition.
- implements default behaviour for the interface common to all classes, as appropriate.
- Leaf (File)
- A leaf has no children.
- defines behavior for primitive objects in the composition.
- Composite (Folder)
- stores child components
- defines/implements behaviour for components having children.
- Client (FileSystem)
- manipulates objects in the composition through the Component interface.
- Clients use the Component class interface to interact with objects in the composite structure.
- If the recipient is a Leaf, then the request is handled directly.
- If the recipient is a Composite, then it usually forwards requests to its child components, possibly performing additional operations before and/or after forwarding.
- Makes the client simple.
- It can apply the same operations over both composites and individual objects (leaves).
- In most cases we can ignore the differences between the composition of objects and leaves.
- Makes it easier to add new kinds of components
- Newly defined Composite or Leaf subclasses work automatically with existing structures and client code.
- In specific cases, it is difficult to restrict the components of the tree to only particular types. Therefore, to enforce such contraint, the program must rely on run-time checks, since it cannot use the type system of programming language.
Definition
Component
interface IFileSystemComponent
{
void PrintName(string prefix);
}
Leaf
class File : IFileSystemComponent
{
public string Name { get; set; }
public File(string name)
{
this.Name = name;
}
public void PrintName(string prefix = "")
{
Console.WriteLine("{0} {1}", prefix, Name);
}
}
Composite
class Folder : IFileSystemComponent
{
private readonly List<IFileSystemComponent> _fsComponents;
public string Name { get; set; }
public Folder(string name)
{
this.Name = name;
this._fsComponents = new List<IFileSystemComponent>();
}
...
public void PrintName(string prefix = "")
{
Console.WriteLine("{0} {1}", prefix, Name);
foreach (var fileSystemComponent in _fsComponents)
{
fileSystemComponent.PrintName(prefix + "\t");
}
}
}
Usage
Folder mainFolder = new Folder("Main Folder");
Folder subFolder1 = new Folder("Sub Folder 1");
Folder subFolder2 = new Folder("Sub Folder 2");
mainFolder.Add(subFolder1);
mainFolder.Add(subFolder2);
mainFolder.Add(new File("File 1 in Main Folder"));
subFolder1.Add(new File("File 1 in Sub Folder 1"));
subFolder1.Add(new File("File 2 in Sub Folder 1"));
subFolder2.Add(new File("File 1 in Sub Folder 2"));
subFolder2.Add(new Folder("Empty folder in Sub Folder 2"));
mainFolder.PrintName();
Output
Main Folder
Sub Folder 1
File 1 in Sub Folder 1
File 2 in Sub Folder 1
Sub Folder 2
File 1 in Sub Folder 2
Empty folder in Sub Folder 2
File 1 in Main Folder
- Chain of Responsibility - Often the component-parent link is used for a Chain of Responsibility.
- Decorator is often used with Composite. When decorators and composites are used together, they will usually have a common parent class. So decorators will have to support the Component interface with operations like Add, Remove, and GetChild.
- Flyweight lets you share components, but they can no longer refer to their parents.
- Iterator can be used to traverse composites.
- Visitor localizes operations and behavior that would otherwise be distributed across Composite and Leaf classes.