In the last post of the series, we took a look at the Observer pattern. This time we’re going to explore the Composite pattern. The Composite pattern gives us the ability to take a complex procedure that may involve many steps and turn it into something that is simple for consumers to use.
The classic definition of the Composite pattern involves three pieces: Component, Leaf, and Composite Component. The component defines the interface that the units (leaves and\or composite components) must implement. A leaf is an implementation of a component that performs work. The composite component also implements the component interface but that’s where the similarities end. Under the covers, it contains a collection of components which could be leaves on nested composites. When an interface method is called, it delegates the call to it’s child components. This may sound more complicated than it actually is. Let’s take a look at an example to see how this could work.
C# Example
In this example, we’re going to take the process of changing a car’s oil and break it down to a group of independent tasks. Following our definition above, let’s define an IComponent interface. Per the contract of this interface, all of our “task” objects will need to include a PerformTask() method.
public interface IComponent { void PerformTask(); }
Next, we’re going to create a base class for the tasks that are made up of a collection of child tasks. Notice that although it implements the PerformTask() method of the IComponent interface, it actually delegates the work to it’s child components.
public abstract class CompositeComponent : IComponent { private IList<IComponent> _tasks = new List<IComponent>(); public void PerformTask() { foreach (var task in _tasks) task.PerformTask(); } public void AddTask(IComponent task) { _tasks.Add(task); } public void RemoveTask(IComponent task) { _tasks.Remove(task); } }
Now let’s create our first needed task (aka. leaf). This one represents the draining of the old oil in the vehicle.
public class DrainOldOilTask : IComponent { public void PerformTask() { Console.WriteLine("Draining old oil."); } }
The next step is to replace the oil filter which is made of two steps, removing the old filter and installing the new one. To demonstrate this, we’ll create a new CompositeComponent subclass.
public class RemoveOldFilterTask : IComponent { public void PerformTask() { Console.WriteLine("Removing old filter."); } } public class InstallNewFilterTask : IComponent { public void PerformTask() { Console.WriteLine("Installing new filter."); } } public class ReplaceFilterTask : CompositeComponent { public ReplaceFilterTask() { AddTask(new RemoveOldFilterTask()); AddTask(new InstallNewFilterTask()); } }
Last, we need to create a task for adding the new oil as well as a composite “ChangeOil” task that ties it all together. Notice that the ChangeOil object is composed of both regular tasks (DrainOilTask and AddNewOilTask) as well as a composite task (ReplaceFilterTask).
public class AddNewOilTask : IComponent { public void PerformTask() { Console.WriteLine("Adding new oil."); } } public class ChangeOil : CompositeComponent { public ChangeOil() { AddTask(new DrainOldOilTask()); AddTask(new ReplaceFilterTask()); AddTask(new AddNewOilTask()); } }
Now the entire process is a simple method call.
new ChangeOil().PerformTask();
Output
Draining old oil.
Removing old filter.
Installing new filter.
Adding new oil.
Ruby Example
With Ruby, we no longer need to define a component interface. We just need ensure that our classes define a perform_work() method. We will, however, want to create a base class for our composite components.
class CompositeComponent def initialize @tasks = [] end def perform_task @tasks.each {|task| task.perform_task} end def add_task(task) @tasks << task end def remove_task(task) @tasks.delete task end end [/source] <p>Now we'll create our task objects. Besides basic syntax, the structure of these classes are pretty much the same C# versions.</p> class DrainOldOilTask def perform_task puts "Draining old oil." end end class RemoveOldFilterTask def perform_task puts "Removing old filter." end end class InstallNewFilterTask def perform_task puts "Installing new filter." end end class ReplaceFilterTask < CompositeComponent def initialize super add_task RemoveOldFilterTask.new add_task InstallNewFilterTask.new end end class AddNewOilTask def perform_task puts "Adding new oil." end end class ChangeOil < CompositeComponent def initialize super add_task DrainOldOilTask.new add_task ReplaceFilterTask.new add_task AddNewOilTask.new end end [/source] <p>Using the objects is also very much the same.</p> ChangeOil.new.perform_task
Output
Draining old oil.
Removing old filter.
Installing new filter.
Adding new oil.
As you can see, implementing the pattern in the two languages is pretty similar. The biggest difference is the lack of need for a component interface within the Ruby example.
Next time, we’ll be looking at the Iterator pattern. Stay tuned!
Leave A Comment