Composite pattern

From Wikipedia, the free encyclopedia

In computer science, the composite pattern is a design pattern: "A general solution to a common problem in software design."

Motivation: In object-oriented programming, a Composite is an object (e.g. a shape) designed as a composition of one-or-more similar objects (other kinds of shapes/geometries), all exhibiting similar functionality. This is known as a "has-a" relationship between objects. The key concept is that you can manipulate a single instance of the object just as you would a group of them. The operations you can perform on all the composite objects often have a least common denominator relationship. For example, when resizing a single shape to fill the screen, surely you would expect/desire that resizing a group of shapes would have the same effect.

When to use: You find that you are using multiple objects in the same way, and often have nearly identical code to handle each of them -- the only differences being that you are manipulating an instance of a 'circle' versus a 'square', for instance. Useful if differentiation doesn't need to exist, and it would be easier to think of them as homogeneous. (Of course, you could still provide functionality to manipulate only a single instance -- like selecting an item from a list instead of operating on the whole list.)

Compose means a special thing: it refers to building objects using DelegationConcept. Delegation-composition hangs onto constituent parts-using references. By contrast, mixins inherit from each part. MixIns prevent returning a WholeObject in response to requests for information, and they prevent having more than one of any given part.

The composite pattern is an object oriented pendant to algebraic data types.

Contents

[edit] Structure

Image:compositepattern.png

Component
  • declares the interface for object composition
  • implements default behaviour
  • declares an interface for accessing and managing the child components
Leaf (Feuille)
represents leaf objects in the composition
Composite
  • defines behaviour for components having children
  • stores child components
  • implements child-related operations to the Component interface
Client
manipulates objects in the composition through the Component interface

[edit] Examples

[edit] C#

The following C# program illustrates the 'Europe' example given above and will output:

(Europe: (England: London) (France: Paris))
using System;
using System.Text;

namespace CompositePattern
{
    class Program
    {
        public static void Main(string[] args)
        {
            Composite england = new Composite("England");
            Leaf york = new Leaf("York");
            Leaf london = new Leaf("London");
            england.AddComponent(york);
            england.AddComponent(london);
            england.RemoveComponent(york);

            Composite france = new Composite("France");
            france.AddComponent(new Leaf("Paris"));

            Composite europe = new Composite("Europe");
            europe.AddComponent(england);
            europe.AddComponent(france);

            Console.WriteLine(europe.DefaultMethod());
            Console.ReadLine();
        }
    }

    public interface IComponent
    {
        string DefaultMethod();
        IComponentCollection GetChildren();
        bool AddComponent(IComponent c);
        bool RemoveComponent(IComponent c);
    }

    // The Java example uses Generics, but these are not available in .Net 1.1
    public class IComponentCollection : System.Collections.CollectionBase
    {
        public bool Add(IComponent o)
        {
            return (base.InnerList.Add(o) >= 0);
        }

        public bool Remove(IComponent o)
        {
            try
            {
                base.InnerList.Remove(o);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public IComponent this[int index]
        {
            get
            {
                return (IComponent)base.InnerList[index];
            }
            set
            {
                base.InnerList[index] = value;
            }
        }
    }

    public class Composite : IComponent
    {
        private string _ID;
        private IComponentCollection _Components = new IComponentCollection();

        public Composite(string identification)
        {
            _ID = identification;
        }

        public string DefaultMethod()
        {
            System.Text.StringBuilder sbOutput = new System.Text.StringBuilder();
            sbOutput.Append(string.Format("({0}:", _ID));
            foreach (IComponent child in GetChildren())
            {
                sbOutput.Append(" " + child.DefaultMethod());
            }
            sbOutput.Append(")");

            return sbOutput.ToString();
        }

        public IComponentCollection GetChildren()
        {
            return _Components;
        }

        public bool AddComponent(IComponent c)
        {
            return _Components.Add(c);
        }

        public bool RemoveComponent(IComponent c)
        {
            return _Components.Remove(c);
        }
    }

    public class Leaf : IComponent
    {
        private string _ID;

        public Leaf(string identification)
        {
            _ID = identification;
        }

        public string DefaultMethod()
        {
            return _ID;
        }

        public IComponentCollection GetChildren()
        {
            return null;
        }

        public bool AddComponent(IComponent c)
        {
            return false;
        }

        public bool RemoveComponent(IComponent c)
        {
            return false;
        }
    }
}


[edit] JAVA

The following JAVA program illustrates the previous.NET example again.

// The "DefaultMethod()" was renamed to be "toString()" in this version.
// requires JDK 1.5 or above, for generics List<E> and foreach loop
// To compile and execute this code:
// javac -cp . Main.java
// java  -cp . Main

import java.util.List;
import java.util.ArrayList;
import java.util.Collection;

public class Main
{
        public static void main(String[] args)
        {
                Composite england = new Composite("England");
                Leaf york = new Leaf("York");
                Leaf london = new Leaf("London");
                england.addComponent(york);
                england.addComponent(london);
                england.removeComponent(york);

                Composite france = new Composite("France");
                france.addComponent(new Leaf("Paris"));

                Composite europe = new Composite("Europe");
                europe.addComponent(england);
                europe.addComponent(france);

                System.out.println(europe.toString());
        }
}

interface IComponent
{
        Collection getChildren();
        boolean addComponent(IComponent c);     // add composite or leaf
        boolean removeComponent(IComponent c);
}

class Composite implements IComponent
{
        private String id;
        private List<IComponent> list = new ArrayList<IComponent> ();

        public Composite(String id)
        {
                this.id = id;
        }

        public String toString()
        {
                StringBuilder buf = new StringBuilder();
                buf.append(String.format("(%s:", id));

                // JDK 1.5 "foreach" implicitly uses an iterator to walk over list
                for (IComponent child : list)
                {
                        buf.append(" " + child.toString());
                }
                buf.append(")");

                return buf.toString();
        }

        //public List<IComponent> getChildren()
        public Collection getChildren()
        {
                return list;
        }

        public boolean addComponent(IComponent c)
        {
                return list.add(c);
        }

        public boolean removeComponent(IComponent c)
        {
                return list.remove(c);
        }
}

class Leaf implements IComponent
{
        private String id;

        public Leaf(String id)
        {
                this.id = id;
        }

        public String toString()
        {
                return this.id;
        }

        public Collection getChildren()
        {
                return null;
        }

        // false because failed to add
        public boolean addComponent(IComponent c)
        {
                return false;
        }

        // false because failed to find it for removal
        public boolean removeComponent(IComponent c)
        {
                return false;
        }
}


[edit] See also

[edit] External links

Parts of this article originated from the Perl Design Patterns Book