Yves Reynhout's Blog
The seagile man
Collection classes and behavior
Lately I've come up with a technique to implement custom finders/queries/filters on my collection classes. Instead of implementing these
finders/queries/filters
directly in the collection class itself, which gets in the way of generating these types of classes anyway (for the record, I am aware of techniques like
CodeSmith
's merge regions), I defined a set of new behaviors:
/// <summary>
/// Behavior for things that can be evaluated.
/// </summary>
public interface
IPredicate
{
/// <summary>
/// Evaluates the specified obj.
/// </summary>
/// <param name="obj">Object to be evaluated.</param>
/// <returns><c>true</c> if obj meets the predicate; otherwise <c>false</c>.</returns>
bool
Evaluate
(object
obj
);
}
/// <summary>
/// Behavior for collections from which items can be extracted using the given <see cref="IPredicate"/>.
/// </summary>
public interface
IExtractable
{
/// <summary>
/// Extracts the items from the collection given the specified predicate, leaving only the items that didn't meet the predicate.
/// </summary>
/// <param name="predicate">Predicate used to evaluate items against.</param>
/// <returns>A collection of items that meet the given predicate.</returns>
object
Extract
(IPredicate
predicate
);
}
/// <summary>
/// Behavior for collections that can be filtered using a given <see cref="IPredicate"/>.
/// </summary>
public interface
IFilterable
{
/// <summary>
/// Filters the items in the collection given the specified predicate, effectively removing the items that match the predicate.
/// </summary>
/// <param name="predicate">Predicate used to evaluate items against.</param>
void
Filter
(IPredicate
predicate
);
}
/// <summary>
/// Behavior for collections from which items can be selected using the given <see cref="IPredicate"/>.
/// </summary>
public interface
ISelectable
{
/// <summary>
/// Selects the items in the collection given the specified predicate but leaves all items in the collection.
/// </summary>
/// <param name="predicate">Predicate used to evaluate items against.</param>
/// <returns>A collection of items that meet the given predicate.</returns>
object
Select
(IPredicate
predicate
);
}
Next I implemented the ISelectable, IExtractable, IFilterable in my collection class template (regardless of the templating mechanism you use). I even provided typed implementations of these methods (using explicit interface declaration).
Foreach rule/condition I've come across in code, I've created a class implementing IPredicate. Feeding instances of my predicate classes to my collection classes allows me to perform either select, extract or filter operations on them. I've even taking this a step further and implemented a CompoundPredicate which basically is a collection of predicates and a predicate decision combinator (IPredicateDecisionCombinator). When a compound condition is passed to one of the select/extract/filter methods, multiple conditions are evaluated at once and the way the result of these evaluations are combined is the concern of the predicate decision combinator (so you can vary from the simple boolean and/or).
There's some room for improvement, namely the Select and Extract could return ICollection or IList instead of object, but that's a matter of taste to me.
The beauty of this solution is that the condition (predicate) is totally separate from the function
(extracting, filtering, selecting)
I want to perform on the collection. Conceptually it is quite similar to Predicate<T> in .NET 2.0 or to IComparer and the Sort method in .NET 1.1 and to some extent to closures. Ofcourse in 1.1 it requires a little more work on your behalf, namely passing around contextual data via the constructor of the predicate whereas in 2.0 anonymous methods will do most of the lifting for you.
Comments
#
Matt Berther
said on 04 April, 2005 08:07 PM
I posted an example of a similar technique based on the Specification pattern by Martin Fowler.
The Specification pattern involves implementing significantly less interfaces to achieve the same functionality.
This is great, though. I love seeing elegant solutions such as this. Keep 'em coming.
#
Yves Reynhout
said on 05 April, 2005 04:31 AM
While I agree this resembles Specification a lot, the code I provide is a better at communicating its intentions [Let's call it a Specification specialization].
#
Matt Berther
said on 07 April, 2005 04:36 PM
You're right. You are providing Filter, Select and Extract interfaces. The Specification pattern would hide that in the object implementation.
#
matt said on 08 April, 2005 02:52 PM
Can the pattern you use here be described also as a "Strategy" pattern; I'm new to Pattern Design principles...it just looked familiar:
Strategy Definition: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Nice work...love this kinds of stuff
#
Yves Reynhout
said on 08 April, 2005 04:30 PM
To some extent this is true, because each of the IPredicate implementations is a "strategy" which can be used "interchangeably". But due to the nature of the IFilterable, ISelectable, IExtractable interfaces, which in each case give the IPredicate implementations another purpose, I'm not very tempted to call it "strategy". So on a technical level it is, but on a conceptual level strategy is not the bell that rings.
#
TrackBack
said on 26 April, 2005 02:13 AM
Leave a Comment
Title
(required)
Name
(required)
Your URL
(optional
)
Comments
(required)
Remember Me?
Search
Go
This Blog
Home
About
Tags
.NET
Code generation
Corporate
Mediator
MVVM
NAuthorize
Object/Relational Mapping
Silverlight
SOA
WPF
Navigation
Home
Blogs
Archives
October 2009 (1)
September 2005 (1)
June 2005 (1)
May 2005 (1)
April 2005 (5)
October 2004 (1)
June 2004 (3)
March 2004 (2)
February 2004 (1)
January 2004 (1)
December 2003 (1)
November 2003 (1)
October 2003 (8)
July 2003 (1)
May 2003 (1)
April 2003 (2)
March 2003 (3)
February 2003 (5)
NAuthorize
NAuthorize on forge.novell.com
Sun's XACML implementation
XACML @ OASIS
RBAC @ NIST
Ravi S. Sandhu
RAD by Healthcare DTF @ OMG
Testdriven.net
Syndication
RSS
Atom
Comments RSS