Design Patterns Series 14 - Composite Pattern

This post will be about the second pattern in the alliance. In the last post we explored first pattern in this alliance, the Iterator Pattern, and today is the time of Composite Pattern.

The Composite Pattern :

The Composite Pattern is all about creating tree-like structures where the leaves in the structure can be treated in the same way as the branches(which are substructures that can contain multiple leaves, as well as other branches). The idea here is that, to make life easier, you should be able to treat the leaves and compositions of leaves in a tree structure the same.

You use the Composite pattern to "Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformally". That is typical of Composite Pattern - when you ask a branch to perform some action, it iterates over all its contained leaves and branches.

The insight behind The Composite Pattern is really about treating the leaves and branches in a tree-like structure the same way, not about tree structures per se.That makes it a heck of a lot easier to work with complex structure like trees because you don't have to use a different set of methods with a part of the structure compared to the whole.

To implement The Composite Pattern, the experts suggests that you use an abstract class as a basis for both the leaves and branches in the tree. Doing so gives the leaves and branches a common set of methods, which is what the Composite Pattern is all about. You can also use an interface instead of abstract class to build it.

Suppose you have a database for hotels chains and you want to report all room type in each hotel in a certain hotels chain. According to the Composite Pattern definition, we can treat hotel chain as leave have branches, hotels, each leave in these branches have a set of leaves, room types.
As you see, we need to iterate over all leaves and branches.First we will create the base for our leaves and branches by create IChain interface which contains the required methods that leaves and branches have to implement. Note the idea here is you will treat the tree shown in the figure not as tree data structure of multiple data types but as tree with the same datatype.



In our example we will treat hotel leaves and room type leaves as chain object so Chain class, Hotel Class, and RoomType class should implement IChain Interface.

IChain interface
public interface IChain
{
    void Add(IChain chain);
    IMyIterator Iterator();
    void Print();
}
The Add() method to add new item(Chain, Hotel , or RoomType),Iterator() to get an iterator that will iterate over current item content, and Print() will print the details of current item.(Note you can get code of the IMyIterator interface by referring to the previous post).

Now create Chain class,Hotel class, and RoomType class that implement IChain interface. Note as we mentioned in the start of this post,the Iterator Pattern and the Composite Pattern form an alliance so actually we implement Iterator Pattern with some modification to treat all leaves and branches the same way.

Chain class
public class Chain : IChain
{
    private List<IChain> chain  = new List<IChain>();
    private String Name { get; set; }
    public Chain(String name)
    {
        Name = name;
    }
    public void Add(IChain chain)
    {
        this.chain.Add(chain);
    }
    public IMyIterator Iterator()
    {
        return new ChainIterator(chain.ToArray());
    }
    public void Print()
    {
        IMyIterator iterator = Iterator();
        Console.WriteLine("Chain Name : {0}" , Name);
        while(iterator.HasNext())
        {
            IChain hotel = (IChain)iterator.Next();
            hotel.Print();
        }
    }
}
Hotel class
public class Hotel : IChain
{
    private IChain[] chains = new IChain[10];
    private Int32 count = 0;

    public Hotel(String name)
    {
        Name = name;
    }
    private String Name { get; set; }
    
    public void Add(IChain chain)
    {
        chains[count++] = chain;
    }
    public IMyIterator Iterator()
    {
        return new ChainIterator(chains);
    }
    public void Print()
    {
        IMyIterator iterator = Iterator();
        while(iterator.HasNext())
        {
            IChain chain = (IChain)iterator.Next();
            chain.Print();
        }
    }
}
RoomType class
 public class RoomType : IChain
 {
     public RoomType(String type,String hotel)
     {
         Type = type;
         Hotel = hotel;
     }
     private String Type { get; set; }
     private String Hotel { get; set; }

     public void Print()
     {
         Console.WriteLine("Type : {0} \t\t\t Hotel : {1}", Type, Hotel);
     }
     public void Add(IChain chain)
     {
         // Do nothing, RoomType has no any collection property to add into
     }
     public IMyIterator Iterator()
     {
         return new RoomTypeIterator(this);
     }
 }




Note the Add() method in RoomType Class do nothing because the room type is the last level in our tree and have not any leaves to add item to it. Also RoomTypeIterator constructor accept just one object, the current, instead of array or list like HotelIterator and ChainIterator respectively.

Now we will implement IMyIterator interface to create two Iterators, one for Chains and Hotels and another for Room Type.You may ask why we don't use one iterator for chains, hotels, and room types. The answer is each one of chains and hotels contain leaves to iterate over but room type have not any leaves so the implementation of IMyIterator interface will be different.

ChainIterator class
public class ChainIterator : IMyIterator
{
    private List<IChain> Chains { get; set; }
    private Int32 index = 0;
    public ChainIterator(IChain[] chains )
    {
        Chains = chains.ToList();
    }

    public object Next()
    {
        return Chains[index++];
    }

    public bool HasNext()
    {
        if (index < Chains.Count() && Chains[index] != null)
        {
            return true;
        }
        return false;
    }
}
RoomTypeIterator class
public class RoomTypeIterator : IMyIterator
{
    private IChain RoomType { get; set; }

    public RoomTypeIterator(IChain roomType)
    {
        RoomType = roomType;
    }

    public object Next()
    {
        return RoomType;
    }

    public bool HasNext()
    {
        return false;
    }
}
Note HasNext() method always return false and Next() method returns the current instance. Now we have built our composite pattern what about testing it and print our tree out.
IChain hiltonchain = new Chain("Hilton");

IChain hiltonCairoHotel = new Hotel("Hilton-Cairo");
hiltonCairoHotel.Add(new RoomType("Single", "Hilton-Cairo"));
hiltonCairoHotel.Add(new RoomType("Double", "Hilton-Cairo"));

IChain hiltonLondonHotel = new Hotel("Hilton-London");
hiltonLondonHotel.Add(new RoomType("Double", "Hilton-London"));
hiltonLondonHotel.Add(new RoomType("Single", "Hilton-London"));
hiltonLondonHotel.Add(new RoomType("Suite", "Hilton-London"));

hiltonchain.Add(hiltonCairoHotel);
hiltonchain.Add(hiltonLondonHotel);
hiltonchain.Print();
In these lines of code we created an chain instance, two Hotel instances, added the room types in each hotel, added the two hotel instances to the chain instance, then we called the Print() method of the chain instance. Yes as you expected it will print out the chain Name then each hotel with its room types list.
Chain Name : Hilton
Type : Single                    Hotel : Hilton-Cairo
Type : Double                    Hotel : Hilton-Cairo
**************************************
Type : Double                    Hotel : Hilton-London
Type : Single                    Hotel : Hilton-London
Type : Suite                     Hotel : Hilton-London
**************************************

you can download the full source code here

Comments

Popular posts from this blog

Android : How to change progress bar color at runtime programmatically?

How to fire RowCommand event of nested GridView?

ASP.Net MVC : Conditional Validation using ValidationAttribute