As usual we can start with the Challenge - Solution approach.
Challenge
You are working on a distributed application containing Head Office and Branch Offices. Each office will be having 2 Clock Controls which needed to be updated globally during Daylight Saving times.
The above diagram shows the current structure. There will be only 1 Head Office and multiple branches under it. The head office and each branch will be having 2 clock controls. The time changing process starts from the head office and in the current approach, the head office has to remember all branches and all clocks inside each branch. This is very tedious and will break when there is consolidation of branches under one branch group.
How to provide a better solution?
Definition
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Implementation
We can use the Composite Design Pattern in the above scenario. Once our solution is implemented the Head Office needs to think about only the Branches. The Branches will think about their respective Clocks.
There are 2 classes in our solution: Office and Clock. The Office class can represent Head Office and Branches. The Clock class manages the clock control for changing time.
We need to introduce one interface which will be implemented by the Office and Clock classes.
interface IComponent
{
void Add (IComponent notifier);
void SetTime(DateTime time);
}
The Add() method ensures adding a component of type IComponent. It can be used to add an Office or Clock instance.
The SetTime() method can be used to change the current time.
Class Implementations
Following are the implementations of the Office and Clock classes:
class Office : IComponent
{
private IList<IComponent> _list = new List<IComponent>();
public void Add(IComponent notifier)
{
_list.Add(notifier);
}
public void SetTime(DateTime time)
{
foreach (IComponent n in _list)
n.SetTime(time);
}
}
class Clock : IComponent
{
public ClockControl ClockControl;
public void Add(IComponent notifier)
{
throw new ApplicationException("You cannot add IClock!");
}
public void SetTime(DateTime time)
{
ClockControl.CurrentTime = time;
}
}
Please note that the SetTime() implementation of the Office class takes care of informing all the added IComponent instances. Thus the Head Office can notify all the branches. The branches will notify all the clock instances.
Creating Instances
The following is the code of instance creation for the Head Office and Branches.
_headOffice = new Office();
_headOffice.Add(new Clock() { ClockControl = hForm.clock1 });
_headOffice.Add(new Clock() { ClockControl = hForm.clock2 });
Office branch1 = new Office();
branch1.Add(new Clock() { ClockControl = b1Form.clock1 });
branch1.Add(new Clock() { ClockControl = b1Form.clock2 });
_headOffice.Add(branch1);
Office branch2 = new Office();
branch2.Add(new Clock() { ClockControl = b2Form.clock1 });
branch2.Add(new Clock() { ClockControl = b2Form.clock2 });
_headOffice.Add(branch2);
Please note that the branch instances are added to the head office.
Running the Application
On clicking the Set Time button you can see all the clocks are reset to 10:00 AM.
Here the Head Office does not need to think about all the clock instances of branches. Here the individual objects (clocks) and composite objects (branches) are treated uniformly satisfying the pattern definition.
Summary
In this article we have explored the Composite Pattern. It provides a centralized approach in talking with individual and combined objects. The attachment contains the example application including the clock control we have discussed.