Enough with that, let's implement one! Based on Bizz-Buzz
using System;
using System.Collections.Generic;
namespace FooBarTest // inspired by a coding test on codinghorror
{
public class Program
{
public static void Main(string[] args)
{
// We'll have a COR service that takes a number of returns a string
// There will be 4 policies in it
// the condition delegate indicates what a handled condition is.
ChainOfResponsability<string,int> cor = new ChainOfResponsability<string,int>(4,(s)=>{return (String.Empty != s);});
// The first stage is the "15" handler. Any int that is evenly
// divisible by 15 will return the string "foobar"
// Order is important. You'll want to order your conditions from
// specific to general
cor.AddPolicy(0, (n) => { return (0 == (n % 15)) ? "Bizz Buzz": String.Empty; });
// The order of second and third stage are not important.
cor.AddPolicy(1, (n) => { return (0 == (n % 5)) ? "Buzz" : String.Empty; });
cor.AddPolicy(2, (n) => { return (0 == (n % 3)) ? "Bizz" : String.Empty; });
// The most general condition. The "Catch ALl".
cor.AddPolicy(3, (n) => { return n.ToString(); });
// Look for "Bizz Buzz" when 15 is a root. "Buzz" for 5, and "Bizz" for 3.
for (int i = 0; i < 100; i++) Console.WriteLine(cor.Evaluate(i).result);
Console.ReadLine();
}
}
public class ChainOfResponsability<RESULT,VALUE>
{
public class Tuple // Results, and which policy handled it
{
public RESULT result { get; private set; }
public bool Handled { get; private set; }
public int Stage { get; private set; }
public policy Policy { get; private set; }
}
public Tuple(bool handled, int stage, policy p, RESULT r)
{
this.result = r; // the result of the policy that handled this
this.Handled = handled; // did the request get handled?
this.Stage = stage; // which stage handled it?
this.Policy = p; // which policy handled it?
}
}
public delegate bool EvalTrue(RESULT x); // function to determine that request handled
public delegate RESULT policy(VALUE y); // a policy function. The heart of our COR service.
public ChainOfResponsability(int stages, EvalTrue ev)
{
this.policies = new policy[stages];
this.CC = ev;
}
private EvalTrue CC; // We store the result evaluation function
private policy[] policies; // the set of policies.
// Add a policy to a specific stage in the pipeline.
// Specifying more than one per stage, is unwise. The later one over-writes.
public void AddPolicy(int stage, policy function)
{
if ((0 <= stage) && (this.policies.Length > stage))
this.policies[stage] = function;
else throw new Exception("invalid stage specified"); // TODO throw a more specific Exception
}
// Evaluate the value against each policy in the pipeline until a policy
// indicates that it has handled it.
// Values that aren't handled will result in the Tuple.Handled == false
}
public Tuple Evaluate(VALUE n)
{
bool handled = false;
RESULT result = default(RESULT); // the result of a policy that handles the value
int invokedStage = -1; // which stage handled it?
policy p = default(policy); // which policy handled it?
// go through each policy in our pipeline
for (int stage = 0; (stage < this.policies.Length && !handled); stage++) {
// If there is a policy at this stage, give it a shot at it
if (null != this.policies[stage]) {
result = this.policies[stage].Invoke(n);
handled = this.CC.Invoke(result); // did the current policy handle this?
if (handled) { // yep. let's remember where and who.
invokedStage = stage;
p = this.policies[stage];
}
}
}
return new Tuple(handled,invokedStage,p,result); // immutable
}
}
}
No comments:
Post a Comment