Chain of Responsibility
The chain of responsibility pattern
The chain of responsibility is a behavioral pattern and quite complex to understand. Basically, the idea is that to keep objects loosely coupled we have a chain of instances where a message (or some data structure) is sent through the chain and one of the objects eventually claims the message and acts on it. The sequence diagram below shows the message (as the arrow) being passed on through the sequence of objects.The class structure diagramatically is:
The chain of command pattern is quite elegant and has similar functionality as a switch ... case but without the ugliness associated with the multi-selection code. Also, similarily to the switch... it needs an action to handle the default case.
Lets try out some code, this is based on the example in Design Patterns [GOF]. We need an enumeration to decide which object will handle the request:
enum HelpTopic {
NO_HELP = -1,
BUTTON = 1,
DIALOG = 2,
APPLICATION = 3
}
After the enum, which is similarily used in a
switch...,
we have a base level abstraction that passes on the request to the next successor,
if there is a successor.
It also handles the default and no help(do nothing) cases.
NO_HELP = -1,
BUTTON = 1,
DIALOG = 2,
APPLICATION = 3
}
abstract class HelpHandler {
private HelpHandler successor;
protected HelpTopic helpTopic;
protected HelpHandler(HelpHandler helpHandler, HelpTopic helpTopic) {
this.successor = helpHandler;
this.helpTopic = helpTopic;
}
public virtual bool HasHelp() {
return helpTopic != HelpTopic.NO_HELP;
}
public virtual void HandleHelp() {
if (successor != null) {
successor.HandleHelp();
} else {
decideWhichDefaultHandle();
}
}
void decideWhichDefaultHandle() { // click to open
}
The 'components' that have to handle requests for help can inherit from
HelpHandler but we end up with duplicate code
so another class that inherits from HelpHandler
is created called, in this instance Widget:
private HelpHandler successor;
protected HelpTopic helpTopic;
protected HelpHandler(HelpHandler helpHandler, HelpTopic helpTopic) {
this.successor = helpHandler;
this.helpTopic = helpTopic;
}
public virtual bool HasHelp() {
return helpTopic != HelpTopic.NO_HELP;
}
public virtual void HandleHelp() {
if (successor != null) {
successor.HandleHelp();
} else {
decideWhichDefaultHandle();
}
}
void decideWhichDefaultHandle() { // click to open
if (HasHelp()) {
Console.WriteLine("At the end of the chain, carried out default behavior.");
} else {
Console.WriteLine("No help needed!");
}
Console.WriteLine("At the end of the chain, carried out default behavior.");
} else {
Console.WriteLine("No help needed!");
}
class Widget : HelpHandler {
protected Widget(HelpHandler helpHandler, HelpTopic helpTopic) : base(helpHandler, helpTopic) { }
public override void HandleHelp() {
if (HasHelp()) {
Console.WriteLine($"The {helpTopic.ToString()} handled the request.");
} else {
base.HandleHelp();
}
}
}
Then a couple of Widgets:
protected Widget(HelpHandler helpHandler, HelpTopic helpTopic) : base(helpHandler, helpTopic) { }
public override void HandleHelp() {
if (HasHelp()) {
Console.WriteLine($"The {helpTopic.ToString()} handled the request.");
} else {
base.HandleHelp();
}
}
}
class Dialog : Widget {
public Dialog(HelpHandler helpHandler, HelpTopic helpTopic) : base(helpHandler, helpTopic) {}
public override bool HasHelp() {
return helpTopic == HelpTopic.DIALOG;
}
}
and
public Dialog(HelpHandler helpHandler, HelpTopic helpTopic) : base(helpHandler, helpTopic) {}
public override bool HasHelp() {
return helpTopic == HelpTopic.DIALOG;
}
}
class Button : Widget {
public Button(Widget widget, HelpTopic helpTopic) : base(widget, helpTopic) {}
public override bool HasHelp() {
return helpTopic == HelpTopic.BUTTON;
}
}
We then have the bottom 'layer' which inherits from HelpHandler and doesn't have much going on
as everything is done at the abstract layer:
public Button(Widget widget, HelpTopic helpTopic) : base(widget, helpTopic) {}
public override bool HasHelp() {
return helpTopic == HelpTopic.BUTTON;
}
}
class Application : HelpHandler {
public Application(HelpTopic helpTopic) : base(null, helpTopic) {
}
}
To show the requests being handled, all originating from button,
I've constructed the following console application:
public Application(HelpTopic helpTopic) : base(null, helpTopic) {
}
}
static void Main(string[] args) {
setHelpHandlers(HelpTopic.DIALOG);
setHelpHandlers(HelpTopic.BUTTON);
setHelpHandlers(HelpTopic.APPLICATION);
setHelpHandlers(HelpTopic.NO_HELP);
Console.ReadKey();
}
static void setHelpHandlers(HelpTopic helpTopic) {
Application application = new Application(helpTopic);
Dialog dialog = new Dialog(application, helpTopic);
Button button = new Button(dialog, helpTopic);
button.HandleHelp();
}
Which gives the following output:
setHelpHandlers(HelpTopic.DIALOG);
setHelpHandlers(HelpTopic.BUTTON);
setHelpHandlers(HelpTopic.APPLICATION);
setHelpHandlers(HelpTopic.NO_HELP);
Console.ReadKey();
}
static void setHelpHandlers(HelpTopic helpTopic) {
Application application = new Application(helpTopic);
Dialog dialog = new Dialog(application, helpTopic);
Button button = new Button(dialog, helpTopic);
button.HandleHelp();
}
The DIALOG handled the request.
The BUTTON handled the request.
At the end of the chain, carried out default behavior.
No help needed!
The BUTTON handled the request.
At the end of the chain, carried out default behavior.
No help needed!
Summary
That's the chain of responsibility a pretty complex pattern that allows a request to be processed by particular objects interested in the request, with the request being passed from object to object.