Can anyone give a practical example of using the design patterns Composite and Chain of Responsibility together?
Thanks
A very practical example is GUI design, for example with the Qt framework.
A QObject can be an individual object or a composite of more objects. QObjects (ideally) know their parent QObject, so they also form a chain of responsibility.
Example: The main window has a dialog (a QObject). The dialog has an input line and a layout-box (all QObjects). The layout box has 2 buttons (all QObjects).
An event to a button (e.g. click) will be passed on through the chain of responsibility until an QObject can handle the event.
The other direction also works (due to the composite design). A show() to the dialog will be passed to the child-objects, so the input-line and the layout-box and the buttons will become visible too.
This example combines Chain of Responsibility, Command, and Composite, and leverages the Try*
method style familiar to .NET.
Given command and handler types:
public interface IResults { }
public interface ICommand { }
public interface IHandler
{
Boolean TryHandle(ICommand command, out IResults results);
}
Given a few IHandler
implementations:
public class FooHandler : IHandler
{
public Boolean TryHandle(ICommand command, out IResults results)
{
// ...
}
}
public class BarHandler : IHandler
{
public Boolean TryHandle(ICommand command, out IResults results)
{
// ...
}
}
And a composite IHandler
implementation:
public class CompositeHandler : IHandler
{
public IList<IHandler> Handlers { get; } = new List<IHandler>();
public Boolean TryHandle(ICommand command, out IResults results)
{
foreach (var handler in this.Handlers) {
if (handler.TryHandle(command, out results)) {
return true;
}
}
results = null;
return false;
}
}
And using it in client code:
var command = /* ... */;
var handler = new CompositeHandler();
handler.Handlers.Add(new FooHandler());
handler.Handlers.Add(new BarHandler());
IResults results;
if (handler.TryHandle(command, out results)) {
// handled
}
else {
// not handled
}
Through the use of generics, type parameterization/constraints can ensure a degree of safety too:
public interface IResults { }
public interface ICommand<TResults>
where TResults : IResults
{
// ...
}
public interface IHandler<TCommand, TResults>
where TCommand : ICommand<TResults>
where TResults : IResults
{
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With