Friday 17 December 2010

Composite Pattern

"Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly."



This pattern is concerned with enabling client code to handle two different objects uniformly, and is best illustrated by building a tree structure.  The slight issue I have with this pattern is that in order to do this you end up either implementing empty Add(), Remove() and GetChild() methods in Leaf classes or you implement them to throw NotImplemented exceptions.  This means that the uniformity really only refers to iterating over the collection.
In the code sample below the FertileDog (Composite) and NeuteredDog (Leaf) classes share the same Dog (Component) interface which means client code doesn't have to "know" which one it has - Dogs are just added to their parents, irrespective of whether they are a NeuteredDog or a FertileDog.  Also Dog is an abstract class and common behaviour has been implemented within it which is shared by its derivatives.  In this case Display() is shared by NeuteredDog and FertileDog.  Note how Dog both uses it (calls base.Display()) and overrides it.

using System;
using System.Collections.Generic;
 
namespace Composite
{
    class Program
    {
        static void Main(string[] args)
        {
            Dog fly = new FertileDog("fly");
            Dog jess = fly.Add(new FertileDog("jess"));
            Dog tess = fly.Add(new FertileDog("tess"));
            Dog bessie = jess.Add(new FertileDog("bessie"));
            Dog nessie = jess.Add(new NeuteredDog("nessie"));
            bessie.Add(new NeuteredDog("caz"));
            bessie.Add(new NeuteredDog("baz"));
            nessie.Add(new NeuteredDog("daz"));
 
            fly.Display(1);
            Console.ReadLine();
        }
 
        public abstract class Dog
        {
            private string _name;
 
            public Dog(string name)
            {
                _name = name;
            }                   
 
            public virtual void Display(int level)
            {
                string showDepth = new string(' ', level);
                Console.Write(string.Concat(showDepth, _name));
                Console.WriteLine();             
            }
          
            public abstract Dog Add(Dog child);
 
            public abstract void Remove(Dog child);
 
            public abstract Dog GetChild(int index);         
        }
 
        public class FertileDog : Dog
        {
            private List<Dog> _litter = new List<Dog>();
 
            public FertileDog(string name):
                base(name)
            {
            }
 
            public override void Display(int level)
            {              
                base.Display(level);
 
                foreach (Dog puppy in _litter)
                {
                    puppy.Display(level + 1);
                }
            }
 
            public override Dog Add(Dog puppy)
            {
                _litter.Add(puppy);
                return puppy;
            }
 
            public override void Remove(Dog puppy)
            {
                _litter.Remove(puppy);
            }
 
            public override Dog GetChild(int index)
            {
                return _litter[index];
            }
        }
 
        public class NeuteredDog : Dog
        {
             public NeuteredDog(string name):
                base(name)
            {
            }
 
            public override Dog Add(Dog puppy)
            {
                return null;
            }
 
            public override void Remove(Dog puppy)
            {
            }
 
            public override Dog GetChild(int index)
            {
                return null;
            }                  
        }
    }
}


Outputs:

|fly
||jess
|||bessie
||||caz
||||baz
|||nessie
||tess

No comments:

Post a Comment