I got a couple of emails with great feedback from my last couple of posts. I think I could probably turn this presentation into 2 different presentations but it is curing my blogging writers block so i'll post the responses here for everyone's benefit.
Shane was very quick to point out that the method signature in Customer is kind of misleading:
public bool IsAnActiveCustomer(ISpecification<Customer> activeCustomerSpecification)
We could actually pass any specification that implements the ISpecification interface into this function. We can refactor a little to make it a little more clear to someone consuming the Customer class:
public bool IsAnActiveCustomer()
{
return this.Matches(new ActiveCustomerSpecification());
}
public bool Matches(ISpecification<Customer> specification)
{
return specification.IsSatisfiedBy(this);
}
Using this approach we supply consumers of our class with a default for an active customer and allow them to specify their own specification for what constitutes an active customer.
One thing that I should have mentioned in my post is that Filter and Map are included in the List class in .NET 2.0. Filter == FindAll and Map==ConvertAll, here is the code from Reflector:
public List<T> FindAll(Predicate<T> match)
{
if (match == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
List<T> list = new List<T>();
for (int i = 0; i < this._size; i++)
{
if (match(this._items[i]))
{
list.Add(this._items[i]);
}
}
return list;
}
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
{
if (converter == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter);
}
List<TOutput> list = new List<TOutput>(this._size);
for (int i = 0; i < this._size; i++)
{
list._items[i] = converter(this._items[i]);
}
list._size = this._size;
return list;
}