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.
Published 05 April 2005 12:03 AM by yreynhout
Filed under: ,

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

(required) 
(required) 
(optional)
(required)