Composite pattern
In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.[1]
Motivation
When dealing with Tree-structured data, programmers often have to discriminate between a leaf-node and a branch. This makes code more complex, and therefore, error prone. The solution is an interface that allows treating complex and primitive objects uniformly. In object-oriented programming, a composite is an object designed as a composition of one-or-more similar objects, all exhibiting similar functionality. This is known as a "has-a" relationship between objects.[2] The key concept is that you can manipulate a single instance of the object just as you would manipulate a group of them. The operations you can perform on all the composite objects often have a least common denominator relationship. For example, if defining a system to portray grouped shapes on a screen, it would be useful to define resizing a group of shapes to have the same effect (in some sense) as resizing a single shape.
When to use
Composite should be used when clients ignore the difference between compositions of objects and individual objects.[1] If programmers find that they are using multiple objects in the same way, and often have nearly identical code to handle each of them, then composite is a good choice; it is less complex in this situation to treat primitives and composites as homogeneous.
Structure
- Component
- is the abstraction for all components, including composite ones
- declares the interface for objects in the composition
- (optional) defines an interface for accessing a component's parent in the recursive structure, and implements it if that's appropriate
- Leaf
- represents leaf objects in the composition .
- implements all Component methods
- Composite
- represents a composite Component (component having children)
- implements methods to manipulate children
- implements all Component methods, generally by delegating them to its children
Variation
As it is described in Design Patterns, the pattern also involves including the child-manipulation methods in the main Component interface, not just the Composite subclass. More recent descriptions sometimes omit these methods.[3]
Example
The following example, written in Java, implements a graphic class, which can be either an ellipse or a composition of several graphics. Every graphic can be printed. In Backus-Naur form,
Graphic = ellipse | GraphicList GraphicList = empty | Graphic GraphicList
It could be extended to implement several other shapes (rectangle, etc.) and methods (translate, etc.).
Java
/** "Component" */
interface Graphic {
//Prints the graphic.
public void print();
}
/** "Composite" */
import java.util.List;
import java.util.ArrayList;
class CompositeGraphic implements Graphic {
//Collection of child graphics.
private List<Graphic> childGraphics = new ArrayList<Graphic>();
//Prints the graphic.
public void print() {
for (Graphic graphic : childGraphics) {
graphic.print();
}
}
//Adds the graphic to the composition.
public void add(Graphic graphic) {
childGraphics.add(graphic);
}
//Removes the graphic from the composition.
public void remove(Graphic graphic) {
childGraphics.remove(graphic);
}
}
/** "Leaf" */
class Ellipse implements Graphic {
//Prints the graphic.
public void print() {
System.out.println("Ellipse");
}
}
/** Client */
public class Program {
public static void main(String[] args) {
//Initialize four ellipses
Ellipse ellipse1 = new Ellipse();
Ellipse ellipse2 = new Ellipse();
Ellipse ellipse3 = new Ellipse();
Ellipse ellipse4 = new Ellipse();
//Initialize three composite graphics
CompositeGraphic graphic = new CompositeGraphic();
CompositeGraphic graphic1 = new CompositeGraphic();
CompositeGraphic graphic2 = new CompositeGraphic();
//Composes the graphics
graphic1.add(ellipse1);
graphic1.add(ellipse2);
graphic1.add(ellipse3);
graphic2.add(ellipse4);
graphic.add(graphic1);
graphic.add(graphic2);
//Prints the complete graphic (four times the string "Ellipse").
graphic.print();
}
}
C#
The following example, written in C#.
namespace CompositePattern
{
using System;
using System.Collections.Generic;
using System.Linq;
//Client
class Program
{
static void Main(string[] args)
{
// initialize variables
var compositeGraphic = new CompositeGraphic();
var compositeGraphic1 = new CompositeGraphic();
var compositeGraphic2 = new CompositeGraphic();
//Add 1 Graphic to compositeGraphic1
compositeGraphic1.Add(new Ellipse());
//Add 2 Graphic to compositeGraphic2
compositeGraphic2.AddRange(new Ellipse(),
new Ellipse());
/*Add 1 Graphic, compositeGraphic1, and
compositeGraphic2 to compositeGraphic */
compositeGraphic.AddRange(new Ellipse(),
compositeGraphic1,
compositeGraphic2);
/*Prints the complete graphic
(four times the string "Ellipse").*/
compositeGraphic.Print();
Console.ReadLine();
}
}
//Component
public interface IGraphic
{
void Print();
}
//Leaf
public class Ellipse : IGraphic
{
//Prints the graphic
public void Print()
{
Console.WriteLine("Ellipse");
}
}
//Composite
public class CompositeGraphic : IGraphic
{
//Collection of Graphics.
private readonly List<IGraphic> graphics;
//Constructor
public CompositeGraphic()
{
//initialize generic Collection(Composition)
graphics = new List<IGraphic>();
}
//Adds the graphic to the composition
public void Add(IGraphic graphic)
{
graphics.Add(graphic);
}
//Adds multiple graphics to the composition
public void AddRange(params IGraphic[] graphic)
{
graphics.AddRange(graphic);
}
//Removes the graphic from the composition
public void Delete(IGraphic graphic)
{
graphics.Remove(graphic);
}
//Prints the graphic.
public void Print()
{
foreach (var childGraphic in graphics)
{
childGraphic.Print();
}
}
}
}
Simple example
//IVSR: Composite example
/// Treats elements as composition of one or more elements, so that components can be separated
/// between one another
public interface IComposite
{
void CompositeMethod();
}
public class LeafComposite :IComposite
{
#region IComposite Members
public void CompositeMethod()
{
//To Do something
}
#endregion
}
/// Elements from IComposite can be separated from others
public class NormalComposite : IComposite
{
#region IComposite Members
public void CompositeMethod()
{
//To Do Something
}
#endregion
public void DoSomethingMore()
{
//Do Something more .
}
}
See also
External links
- Composite pattern description from the Portland Pattern Repository
- Composite pattern in UML and in LePUS3, a formal modelling language
- Class::Delegation on CPAN
- "The End of Inheritance: Automatic Run-time Interface Building for Aggregated Objects" by Paul Baranowski
- PerfectJPattern Open Source Project, Provides componentized implementation of the Composite Pattern in Java
- [1] A persistent Java-based implementation
- Composite Design Pattern
- Composite Design Pattern implementation example
References
- ^ a b Gamma, Erich; Richard Helm; Ralph Johnson; John M. Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. p. 395. ISBN 0-201-63361-2.
- ^ Scott Walters (2004). Perl Design Patterns Book.
- ^ Geary, David (13 Sep 2002). "A look at the Composite design pattern".