Abstract
Design Patterns, Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides [also known as the Gang of Four (GOF)] has been a de facto reference for any Object-Oriented software developer. This article is Part IV of a series of articles illustrating the GOF Design Patterns in C#. We will discuss the Chain of Responsibility, Command, and Interpreter patterns. The next few articles will conclude the C# illustrations of the GOF design patterns. It is assumed the reader is familiar with basic C# syntax and conventions, and not necessarily details of the .NET Framework.
Background
In Design Patterns, each pattern is described with its name (and other well-known names); the motivation behind the pattern; its applicability; the structure of the pattern; class/object participants; participant collaborations; pattern consequences; implementation; sample code; known uses; and related patterns. This article will only give a brief overview of the pattern, and an illustration of the pattern in C#.
A design pattern is not code, per se, but a "plan of attack" for solving a common software development problem. The GOF had distilled the design patterns in their book into three main subject areas: Creational, Structural, and Behavioral. This article deals with the Behavioral design patterns, or how objects are act together. The first three articles in this series dealt with the Creational and Structural patterns. This article begins the illustration of the Behavioral patterns as described by the Gang of Four.
This article is meant to illustrate the design patterns as a supplement to their material. It is recommended that you are familiar with the various terms and object diagram methods used to describe the design patterns as used by the GOF. If you're not familiar with the diagrams, they should be somewhat self-explanatory once viewed. The most important terms to get your head around are abstract and concrete. The former is a description and not an implementation, while the latter is the actual implementation. In C#, this means an abstract class is an interface, and the concrete class implements that interface.
Behavioral Patterns
To quote the GOF, "Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. Behavioral patterns describe not just patterns of objects or classes but also the patterns of communication between them. These patterns characterize complex control flow that's difficult to follow at run-time. They shift your focus away from flow of control to let you concentrate just on the way objects are interconnected."
Chain of Responsibility
There are times when designing software that one comes across the need to pass requests between objects, and multiple objects at that. The Chain of Responsibility pattern allows more than one object to handle a request. Request receivers are "chained" until an object handles it. You move from a specific request handler to the generic, passing the request from object to object until it is finally handled.
A good example is when you throw
an exception from a method. You first get a chance to handle it in a local try
/ catch
block. If it isn't handled locally, the exception passes to the caller, and so on up the chain until it is handled. At last, if there are no application exception handlers, the runtime finally handles it.
The structure of the Chain of Responsibility looks like this:
For our illustration (chain.cs
), we have the following scenario: We're developing a bug tracking system for a software company, and depending on the type of bug (UI, functionality, et al.) it must get handled, routed and so on, but at the least it must be submitted to a bug database. We begin with defining a BugType
structure for enumerating the types of bugs, and a BugHandler
which will be the base class for handling:
public enum BugType { Any, Feature, UI }
public class BugHandler
{
private BugHandler m_oSuccessor;
public BugHandler(BugHandler o) { m_oSuccessor = o; }
virtual public void HandleBug(BugType t)
{
if (m_oSuccessor != null)
{
Console.WriteLine("...{0} passing to successor {1}",
this.GetType().ToString(), m_oSuccessor.GetType().ToString());
m_oSuccessor.HandleBug(t);
}
else throw new Exception("Bug not handled!");
}
}
Our default handling routine HandleBug
just passes along the request, throwing an error if no successor exists. Our derived classes will call base.HandleBug()
if they do not handle the specific bug type:
public class FeatureBugHandler : BugHandler
{
public FeatureBugHandler(BugHandler o) : base(o)
{
}
override public void HandleBug(BugType t)
{
if (BugType.Feature == t)
Console.WriteLine("--> FeatureBugHandler: {0}", t.ToString());
else base.HandleBug(t);
}
}
The client can then "build" a chain of handlers (in any order, mind you). In the sample code, we create two chains, and fire off HandleBug
for each BugType
. The output looks like this:
Chain 1 UI:
--> UIBugHandler: UI
Chain 2 UI:
...Concrete.FeatureBugHandler passing to successor Concrete.UIBugHandler
--> UIBugHandler: UI
Chain 1 Feature:
...Concrete.UIBugHandler passing to successor Concrete.FeatureBugHandler
--> FeatureBugHandler: Feature
Chain 2 Feature:
--> FeatureBugHandler: Feature
Chain 1 Any:
...Concrete.UIBugHandler passing to successor Concrete.FeatureBugHandler
...Concrete.FeatureBugHandler passing to successor Concrete.GenericBugHandler
--> GenericBugHandler: Any
Chain 2 Any:
...Concrete.FeatureBugHandler passing to successor Concrete.UIBugHandler
...Concrete.UIBugHandler passing to successor Concrete.GenericBugHandler
--> GenericBugHandler: Any
One could just as easily create and chain together drawing handlers for various UI elements, or application-specific events. If one wished, you could use a .NET event that "kicks off" the chain.
Command
There are times when developing an application framework that you need to pass requests between objects without knowing anything about operation requested or the receiver of the request. By encapsulating the request as an object itself, one can "parameterize clients with different requests, queue or log requests, and support undoable operations." (GOF) This is the Command behavioral design pattern. You can separate the requesting object from the object that "knows" how to fulfill it.
The Command pattern structure:
The GOF recommend using the Command pattern when you want to:
- parameterize objects by an action to perform (much like
SqlCommand
s allowing different types of command text and execute methods)
- specify, queue, and execute requests at different times. Your Command object may live independently of a request
- support undo. State can be stored on the Command object, and an "undo" operation performs a "reverse" of the execute. For unlimited undo/redo, a list of Commands could be maintained, and one could traverse it backwards or forwards calling "undo" or "execute" appropriately
- build systems from "primitive or atomic operations" such as transactions
To illustrate using this pattern (command.cs
,) suppose we are modelling flying an airplane, specifically, tuning the communication radios. We want to be able to set a frequency, but later be able to go back to a previous one. We'll define an interface for the radio command:
public interface RadioCommand
{
void Execute(double f);
void Undo();
}
In the future, we may be performing more than just tuning a radio frequency with a radio command. Our concrete implementation of this command will perform some action on our radio class, namely setting the desired frequency, or "undoing" the change:
public class ChangeFreqCommand : RadioCommand
{
private double m_fOld;
private Radio m_oRadio;
public ChangeFreqCommand(Radio o)
{
m_oRadio = o;
m_fOld = 0.0;
}
public void Execute(double f)
{
m_fOld = m_oRadio.Frequency;
m_oRadio.Frequency = f;
}
public void Undo()
{
m_oRadio.Frequency = m_fOld;
}
}
Our client will create and use ChangeFreqCommand objects to play with a radio:
ArrayList cmds = new ArrayList();
Radio r = new Radio("Garmin COM", 121.5);
ChangeFreqCommand c = new ChangeFreqCommand(r);
c.Execute(125.25);
cmds.Add(c);
c = new ChangeFreqCommand(r);
c.Execute(121.9);
cmds.Add(c);
To undo, we just iterate through our ArrayList backwards and call Undo()
:
cmds.Reverse();
foreach(ChangeFreqCommand x in cmds) x.Undo();
It's worthwhile to note that we are not checking for bogus Radio or Command states, so the undo/redo may not always work as planned (see my remarks in the Conclusion of this article.)
Interpreter
I've worked on projects where there was a need to tokenize input, and store it in an application's "grammar." The Interperter pattern's intent is "Given a language, define a represention for its grammar along with an interpreter that uses the representation to interpret sentences in the language." (GOF) This is quite useful if you are assembling commands for batch processing that need a more "human" way of inputting controlling statements to your batch processes, or just plain searching a string. You are building or using a syntax "tree," based on the grammar. The Interpreter structure can be represented as follows:
This is probably one of the more "fuzzy" patterns out there, since you have to define a grammar, get it into "computer" form, then interpret it. Probably the best way to illustrate this pattern is to consider a simple RPN calculator.
A Reverse Polish Notation [RPN] calculator operates by placing the elements of an operation before the operation itself. For example:
5 4 +
would yield
9
, or
3 4 *
which would yield
12
.
For our sample (interpreter.cs
,) our grammar is simple: We take integers and operators as input. We regard an the addition operator (+) or an integer to be a terminal expression (it is composed of nothing else,) and a full line of input passed to the parser to be our only non-terminal expression. We won't support nested operations (e.g., in parenthesis.) Our context will be a stack (ArrayList) of inputted integers or the result from an addition. The expression classes would look like this:
public interface AbstractExpression
{
void Interpret(ArrayList c);
}
public class NonTerminalExpression : AbstractExpression
{
public ArrayList expressions;
public NonTerminalExpression()
{
expressions = new ArrayList();
}
public void Interpret(ArrayList c)
{
foreach (AbstractExpression e in expressions)
e.Interpret(c);
}
}
public class IntegerExpression : AbstractExpression
{
public int literal;
public IntegerExpression(int n)
{
literal = n;
}
public void Interpret(ArrayList c)
{
c.Add(literal);
Console.WriteLine("...pushed {0}", literal);
}
}
public class AdditionTerminalExpression : AbstractExpression
{
public void Interpret(ArrayList c)
{
int n = 0;
foreach (int i in c) n += i;
Console.WriteLine("==> {0}", n);
c.Clear();
c.Add(n);
Console.WriteLine("...cleared stack, pushed {0}", n);
}
}
If we call the parser from the client as follows:
RPNParser.Parse("55 32 + 29 441 +")
We should expect to see the following results:
Parsing '55 32 + 29 441 +'
...pushed 55
...pushed 32
==> 87
...cleared stack, pushed 87
...pushed 29
...pushed 441
==> 557
...cleared stack, pushed 557
In this illustration, the syntax tree was simple, since the only non-terminal expression is composed of integer literal expressions, and the addition expressions. The parser (see the source code) splits up the input string and tries to create an integer literal. Failing that, it tries for an addition expression. All other input is ignored. Once the full input has been evaluated, it calls Interpret() on the non-terminal expression. With some modification (a real stack; a better parser; additional non-terminal expression classes,) one can create a fully functional RPN calculator which includes parenthesis and other fun RPN functionality. (I leave that as an exercise to the reader.)
Conclusions
Behavioral design patterns allow for great flexibility in how your software behaves:
The Chain of Responsibility frees objects from knowing which other objects handle a request, objects know the requests will be handled appropriately. The sender and receiver don't need explicit knowledge of each other. It can greatly expand the flexibility in what responsibilities you assign to objects. You do, however, have to be aware that there is no guarantee that a request will be handled, since there is no explicit receiver. It can "fall off the end of the chain..." (GOF)
The Command pattern allows one to):
- Decouple the object that invokes an operation from the one that knows how to perform it.
- Command objects can be extended like any other first-class object
- You can assemble Command objects into a composite to create a "Macro Command"
- New Commands can be created easily without changing existing classes
When using the Command pattern, there are decisions to be made:
- How "deep" or "intellegent" is the command? The
System.SqlClient.SqlCommand
object is one extreme where it does much of the work by itself. Yours may simply bind a receiver and actions for the request.
- How do you represent state for undo/redo, and how do you eliminate errors from accumulating? If you delete a file, how do you "undelete" it? What happens if it is deleted outside the control of your application?
The Interpreter design pattern helps us to "figure out" a language and represent its statements in so we can interpret them in code. It works best when your language is simple and when you're not overly concerned about efficiency. For complex languages, using a parser generator is a more viable option. Design Patterns has additional insights into the Interpreter pattern and nice examples in Smalltalk and C++.
Stay tuned for future articles...
Building the Samples
Unzip the source files to the folder of your choice. Start a shell (cmd.exe) and type nmake
. You may have to alter the Makefile
to point to the correct folder where your .NET Framework libraries exist.
History
2003-04-01 Corrected introduction
2002-11-14 Typo.Fix();
2002-11-13 Initial Revision
References
- Design Patterns, Elements of Reusable Object-Oriented Software. Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Addison Wesley Longman, Inc. 1988. ISBN 0-201-63498-8.