by Moridin8
12. April 2008 20:20
[For a bod in ##csharp]
** In an attempt to try and make things a little more 'realistic' I have added a real example of the use of this pattern below the basic example given here.
namespace ooppatterns
{
using System;
internal class Program
{
private static void Main( string[] args )
{
// Build responsibility chain
MailRoom MR = new MailRoom();
MR.Next = new SmallOrderDepartment();
MR.Next.Next = new LargeOrderDepartment();
MR.Next.Next.Next = new WarehouseOrderDepartment();
// Process incomming mail...
MR.ProcessRequest( new Mail( OrderType.CompanyOrder ) );
Console.WriteLine( new string( '-', 60 ) );
MR.ProcessRequest( new Mail( OrderType.WarehouseOrder ) );
Console.WriteLine( new string( '-', 60 ) );
MR.ProcessRequest( new Mail( OrderType.None ) );
Console.WriteLine( new string( '-', 60 ) );
MR.ProcessRequest( new Mail( OrderType.HomeOrder ) );
Console.ReadLine();
}
}
internal enum OrderType
{
None,
HomeOrder,
ProfessionalOrder,
CompanyOrder,
WarehouseOrder
}
class Mail
{
public OrderType Order;
public Mail( OrderType order )
{
this.Order = order;
}
}
abstract class Handler
{
private Handler _next;
public Handler Next
{
get { return this._next; }
set { this._next = value; }
}
public abstract void ProcessRequest( Mail letter );
}
// Handlers with specific Responsibility criteria...
class MailRoom : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.None )
Console.WriteLine( "Mis-mailed... MailRoom handles" );
else if ( base.Next != null )
{
Console.WriteLine
( "MailRoom can not handle this. Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
class SmallOrderDepartment : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.HomeOrder
|| letter.Order == OrderType.ProfessionalOrder )
Console.WriteLine( "SmallOrderDepartment handled order" );
else if ( base.Next != null )
{
Console.WriteLine( "SmallOrderDepartment can not handle this."
+ @" Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
class LargeOrderDepartment : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.CompanyOrder )
Console.WriteLine( "LargeOrderDepartment handled order" );
else if ( base.Next != null )
{
Console.WriteLine( "LargeOrderDepartment can not handle this."
+ @" Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
class WarehouseOrderDepartment : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.WarehouseOrder )
Console.WriteLine( "WarehouseOrderDepartment handled order" );
else if ( base.Next != null )
{
Console.WriteLine( "LargeOrderDepartment can not handle this."
+ @" Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
}
InFix Notation Parser
I resurrected my InFix parser routine from my CMS code snippet library, removed a couple of things and presented for the site. This is a prime example of the Chain pattern in use. Please be aware of a few things though.
- The operator precedence method is just a dumb switch routine. I removed the precedence parser because it wouldn't work after I removed the things I did for the example. It is *not* fully tested. It seems to work though.
- Due to the way that the partner PostFix routine works, negations are parsed to the character 'N'.
- The PostFix routine that partner's this example is the example I am using for the Interpreter Pattern. Please feel free to meld the two.
Enjoy...
namespace ooppatterns
{
using System;
using System.Collections.Generic;
using System.Text;
internal class Program
{
private static void Main( string[] args )
{
string infix = "8++ + -(1+4) * -200";
// Instantiate a parser package
Package cont = new Package( infix );
// Create InFixParser and chain responsibilities
InFixParser IFP = new InFixParser();
IFP.Next = new Number();
IFP.Next.Next = new Parenthesis();
IFP.Next.Next.Next = new Operators();
IFP.Next.Next.Next.Next = new UnknownCharacters();
// Parse
IFP.Parse( cont );
// Now take results from the PostFix queue and
// convert to a string for viewing.
StringBuilder SB = new StringBuilder();
while ( cont.PostFix.Count > 0 )
SB.Append( cont.PostFix.Dequeue() + " " );
Console.WriteLine( SB.ToString() );
Console.ReadLine();
}
// Infix parser package. Holds all information pertinant
// to the conversion of InFix to PostFix
class Package
{
private Queue<string> _postFix = new Queue<string>();
private Stack<string> _operator = new Stack<string>();
private Stack<char> _unary = new Stack<char>();
private int _parenthesis = 0;
private bool _lastParsedAnOp = false;
private string _infix;
private int _i;
public Package( string infix )
{
this._infix = infix;
}
public Queue<string> PostFix
{
get { return this._postFix; }
}
public Stack<string> Operator
{
get { return this._operator; }
}
public Stack<char> Unary
{
get { return this._unary; }
}
public int Parenthesis
{
get { return this._parenthesis; }
set { this._parenthesis = value; }
}
public bool LastParsedAnOp
{
get { return this._lastParsedAnOp; }
set { this._lastParsedAnOp = value; }
}
public void SkipNextChar()
{
this._i++;
}
public char ReadNextChar()
{
return this._infix[this._i++];
}
public char PeekNextChar()
{
if ( this.IsEOF )
return '\0';
return this._infix[this._i];
}
public bool IsEOF
{
get { return !( this._i < this._infix.Length ); }
}
}
abstract class Handler
{
private Handler _next;
public Handler Next
{
get { return this._next; }
set { this._next = value; }
}
public abstract void Parse( Package con );
}
class InFixParser : Handler
{
// this handler simply pushes tasks along the chain
// and finishes the work left by the other links.
public override void Parse( Package con )
{
while ( !con.IsEOF )
base.Next.Parse( con );
// clear out operator stack...
while ( con.Operator.Count > 0 )
con.PostFix.Enqueue( con.Operator.Pop().ToString() );
}
}
class Number : Handler
{
// Handles numeric values
public override void Parse( Package con )
{
char ch = con.PeekNextChar();
if ( ( ch >= '0' && ch <= '9' ) || ch == '.' )
{
List<char> tb = new List<char>();
// get the whole number
for ( ; ( ch >= '0' && ch <= '9' ) || ch == '.' || ch == '-';
ch = con.PeekNextChar()
)
tb.Add( con.ReadNextChar() );
// Handle negation
if ( con.Unary.Count != 0 && con.Parenthesis == 0 )
{
con.Unary.Pop();
con.PostFix.Enqueue( "N" + new String( tb.ToArray() ) );
}
else
con.PostFix.Enqueue( new String( tb.ToArray() ) );
con.LastParsedAnOp = false;
}
else if ( base.Next != null )
base.Next.Parse( con );
}
}
class Parenthesis : Handler
{
// Handles any brackets (Parenthesis)
public override void Parse( Package con )
{
char ch = con.PeekNextChar();
if ( ch == '(' )
{
con.Operator.Push( ch.ToString() );
con.Parenthesis++;
con.SkipNextChar();
con.LastParsedAnOp = false;
}
else if ( ch == ')' )
{
while ( con.Operator.Count > 0 && ( con.Operator.Peek() ) != "(" )
con.PostFix.Enqueue( con.Operator.Pop().ToString() );
con.Operator.Pop();
con.Parenthesis--;
// place any unary operators in queue.
if ( con.Unary.Count > 0 )
con.PostFix.Enqueue( con.Unary.Pop().ToString() );
con.SkipNextChar();
con.LastParsedAnOp = false;
}
else if ( base.Next != null )
base.Next.Parse( con );
}
}
class Operators : Handler
{
// handle operators
public override void Parse( Package con )
{
char ch = con.PeekNextChar();
if ( ( ch >= '%' && ch <= '/' || ch == '!' || ch == '~' )
&&
ch != '.' && ch != '(' && ch != ')'
)
if ( con.LastParsedAnOp || con.PostFix.Count == 0 )
{
// if this just previously handled an operator,
// then this one must be unary
if ( ch == '-' )
con.Unary.Push( 'N' );
else if ( Precedence( ch.ToString() ) == 90 )
con.Unary.Push( ch );
else
throw new InvalidOperationException();
con.SkipNextChar();
con.LastParsedAnOp = false;
}
else
{
// Process operator...
List<char> tb = new List<char>();
for ( ; tb.Count < 2 && ( ch >= '%' && ch <= '/' )
&& ch != '.' && ch != '(' && ch != ')';
ch = con.PeekNextChar()
)
tb.Add( con.ReadNextChar() );
string token = new String( tb.ToArray() );
string top = "";
int opPrecA = Precedence( token );
// handle post increment/decrement operators
if ( opPrecA == 100 )
{
con.PostFix.Enqueue( token );
return;
}
// Discover precedence of operator and sort stack accordingly.
//
// NB: the precedence stack sorter is not fully tested!
//
if ( con.Operator.Count > 0 )
top = con.Operator.Peek();
int opPrecB = Precedence( top );
if ( opPrecA > opPrecB )
con.Operator.Push( token );
else
{
while ( opPrecB >= opPrecA )
{
string tmp = con.Operator.Pop();
top = con.Operator.Peek();
con.Operator.Push( tmp );
opPrecB = Precedence( top );
}
con.Operator.Push( top );
}
con.LastParsedAnOp = true;
}
else if ( base.Next != null )
base.Next.Parse( con );
}
// Get operator precedence
// NB: the precedence stack sorter is not fully tested!
private static int Precedence( string ch )
{
switch ( ch )
{
case "++":
case "--":
return 100;
case "!":
case "~":
return 90;
case "*":
case "/":
case "%":
case "^":
case "&":
case "|":
return 80;
case "+":
case "-":
return 70;
case "<<":
case ">>":
return 60;
case "<":
case ">":
case "<=":
case ">=":
return 40;
case "==":
case "!=":
case "<>":
return 30;
case "&&":
return 20;
case "||":
return 10;
default:
return 0;
}
}
}
class UnknownCharacters : Handler
{
// handle other characters (ignore)
public override void Parse( Package con )
{
con.SkipNextChar();
}
}
}
}