Design by Contract with AOP Revisited

posted @ Wednesday, October 19, 2005 8:43 PM

 

Last night I posted a quick example of how we could place runtime requirements on methods. This is a common need by developers. I have since updated my example to clean it up a bit and make it more useable. The cleanup should make it much more extensible. In this example, I am placing two restrictions on our parameter. In order to call our function, the parameter being passed to it must be within both the minimum and maximum range defined in our PrologAttribute. What are your thoughts?

 

using System;
using
System.Reflection;
using
AopAlliance.Intercept;
using
Spring.Aop.Framework;
using
Spring.Aop.Interceptor;
using
Microsoft.VisualStudio.TestTools.UnitTesting;

namespace
DeveloperNotes
{
public class ConstraintInterceptor : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
MethodInfo mi
= invocation.Method;
if
(mi != null)
{
Object[] attrs
= mi.GetCustomAttributes(typeof(PrologAttribute), true);
if
(attrs != null && attrs.Length > 0)
{
bool results = false;
foreach
(PrologAttribute pl in attrs)
{
if (pl != null)
{
IEvaluator eval
= pl.Evaluator as IEvaluator;
if
(eval != null)
{
object[] args = invocation.Arguments;
results = eval.Evaluate(args[pl.Index]);
if
(!results)
break;
else
continue;
}
}
}
if(results)
return invocation.Proceed();
else
return null;
}
else
{
return invocation.Proceed();
}
}
return null;
}
}

public interface IEvaluator
{
bool Evaluate(object arg);
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple
=true, Inherited=true)]
public class PrologAttribute : Attribute
{
private int index;
public
IEvaluator evaluator;

public
PrologAttribute() { }

public PrologAttribute(int index, Type evalType, params object[] args)
{
this.Index = index;
Object obj = Activator.CreateInstance(evalType, args);
if
(obj != null)
this.evaluator = obj as IEvaluator;
}

public IEvaluator Evaluator
{
get { return evaluator; }
set { evaluator = value; }
}

public int Index
{
get { return index; }
set { index = value; }
}

public bool Evaluate(object arg)
{
return evaluator.Evaluate(arg);
}
}

public class StringLengthEvaluator : IEvaluator
{
private int min = 0;
private int
max = 0;

public
StringLengthEvaluator(int min, int max)
{
this.min = min;
this
.max = max;
}

public bool Evaluate(object arg)
{
int len = arg.ToString().Length;
return
(len <= max) && (len >= min);
}
}

public class ObjectFactory
{
public static K CreateObject<T, K>()
{
T obj
= Activator.CreateInstance<T>();
if
(obj != null)
{
ProxyFactory factory
= new ProxyFactory(obj);
factory.AddAdvice(new ConstraintInterceptor());
return
(K)factory.GetProxy();
}
return default(K);
}
}

public interface IPerson
{
void SayHello(string name);
}

public class Person : IPerson
{

[Prolog(
0, typeof(StringLengthEvaluator), 0, 5)]
public void SayHello(string name)
{
Console.WriteLine(
"Hello {0}", name);
}
}

class Program
{
static void Main(string[] args)
{
IPerson p
= ObjectFactory.CreateObject<Person, IPerson>();
if
(p != null)
{
p.SayHello(
"Nicholas");
p.SayHello("Nick");
}
Console.Read()
;
}
}
}