Design patterns in C# – part 4 (Composite pattern)

Role

The Composite pattern arranges structured hierarchies so that single components and groups of components can be treated in the same way.

Design

Composite Pattern Image

Implementation

  • IComponent -> IContainer<T>
  • Composite -> ControlContainer
  • Component -> Control
  • Client -> TestCompositePattern
namespace CompositePattern
{
    public interface IControl
    {
        string Name { get; set; }
        void Add(IControl control);
        IControl Remove(T member);
        IControl Find(T member);
        void Show();
    }
}
using System;

namespace CompositePattern
{
    public class Control : IControl
    {
        public Control(string name)
        {
            this.Name = name;
        }

        #region IControl Members

        public string Name { get; set; }

        public void Add(IControl control)
        {
            Console.WriteLine("Cannot add to an Control item");
        }

        public IControl Remove(T member)
        {
            Console.WriteLine("Cannot remove directly");
            return this;
        }

        public IControl Find(T member)
        {
            return member.Equals(this.Name) ? this : null;
        }

        public void Show()
        {
            Console.WriteLine("Control \"{0}\" is shown !", this.Name);
        }

        #endregion
    }
}
using System;
using System.Collections.Generic;

namespace CompositePattern
{
    public class ControlContrainter : IControl
    {
        private readonly List&lt;icontrol&gt; _members;

        public ControlContrainter(string name)
        {
            this.Name = name;
            this._members = new List&lt;icontrol&gt;();
        }

        #region IControl Members

        public void Add(IControl control)
        {
            this._members.Add(control);
        }

        public IControl Remove(T member)
        {
            IControl control = this.Find(member);

            if (control == null)
            {
                return this;
            }

            this._members.Remove(control);
            return control;
        }

        public IControl Find(T member)
        {
            if (this.Name.Equals(member))
            {
                return this;
            }

            IControl found = null;

            foreach (var control in this._members)
            {
                found = control.Find(member);
                if (found != null)
                {
                    break;
                }
            }

            return found;
        }

        public void Show()
        {
            Console.WriteLine("Control \"{0}\" is shown !", this.Name);

            foreach (var member in this._members)
            {
                member.Show();
            }
        }

        public string Name { get; set; }

        #endregion
    }
}
using System;

namespace CompositePattern
{
    public class TestCompositePattern
    {
        internal const string ContainerNameFirst = "First Contrainter";
        internal const string ContainerNameOther = "Other Contrainter";
        internal const string ControlNameFirst = "First";
        internal const string ControlNameSecond = "Second";
        internal const string ControlNameThird = "Third";
        internal const string SplitString = "-------------------------------------------";

        public static void Main(string[] args)
        {
            IControl control1 = new Control(ControlNameFirst);

            Console.WriteLine("Draw control ({0})...", ControlNameFirst);

            control1.Show();
            Console.WriteLine(SplitString);

            IControl control2 = new Control(ControlNameSecond);
            IControl control3 = new Control(ControlNameThird);

            IControl container = new ControlContrainter(ContainerNameFirst);

            container.Add(control1);
            container.Add(control2);
            container.Add(control3);

            Console.WriteLine("Draw control ({0}) ...", ContainerNameFirst);

            container.Show();
            Console.WriteLine(SplitString);

            Console.WriteLine("Find control ({0}) and call Show() on them ...", ControlNameSecond);

            container.Find(ControlNameSecond).Show();
            Console.WriteLine(SplitString);

            Console.WriteLine("Remove exist control ({0}): {1}", ControlNameFirst, container.Remove(ControlNameFirst).Name);
            Console.WriteLine("Remove not exist control ({0}): {1}", ControlNameFirst, container.Remove(ControlNameFirst).Name);
            Console.WriteLine(SplitString);

            IControl containerOther = new ControlContrainter(ContainerNameOther);
            containerOther.Add(container);

            Console.WriteLine("Draw control ({0}) ...", ContainerNameOther);
            containerOther.Show();
            Console.WriteLine(SplitString);
        }
    }
}

OUTPUT:

Draw control (First)…
Control “First” is shown !
——————————————-
Draw control (First Contrainter) …
Control “First Contrainter” is shown !
Control “First” is shown !
Control “Second” is shown !
Control “Third” is shown !
——————————————-
Find control (Second) and call Show() on them …
Control “Second” is shown !
——————————————-
Remove exist control (First): First
Remove not exist control (First): First Contrainter
——————————————-
Draw control (Other Contrainter) …
Control “Other Contrainter” is shown !
Control “First Contrainter” is shown !
Control “Second” is shown !
Control “Third” is shown !
——————————————-

Use when

You have:

  • An irregular structure of objects and composites of the objects

You want:

  • Clients to ignore all but the essential differences between individual objects and composites of objects
  • To treat all objects in a composite uniformly

But consider using as well:

  • The Decorator pattern to provide operations like Add, remove, and Find
  • The Flyweight pattern to share components, provided the notion of “where i am” can be disregarded and all operations start at the root of the composite
  • The Visitor pattern to localize the operations that are currently distributed between the Composite and Component classes

Bibliography

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s