Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Composite + Chain of Responsibility example

Can anyone give a practical example of using the design patterns Composite and Chain of Responsibility together?

Thanks

like image 534
Fabio Avatar asked Dec 28 '22 23:12

Fabio


2 Answers

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.

like image 187
Sascha Avatar answered Jan 05 '23 16:01

Sascha


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
{
    // ...
}
like image 35
Dan Lugg Avatar answered Jan 05 '23 15:01

Dan Lugg