Last week I went and checked out JP's latest presentation on Generics at the Calgary .NET User Group, and as usual it was awesome! He's definitely knee deep in C# 3.0, and was dropping lambda's and extension methods like it was old news... Here's some of the stuff I learned.

Extending the ISpecification interface via the use of Extension methods.

 1     public static class SpecificationExtensions {
 2         public static ISpecification< T > And< T >( this ISpecification< T > leftSide, ISpecification< T > rightSide ) {
 3             return new AndSpecification< T >( leftSide, rightSide );
 4         }
 5 
 6         public static ISpecification< T > And< T >( this ISpecification< T > left, Predicate< T > criteriaToSatisfy ) {
 7             return left.And( new Specification< T >( criteriaToSatisfy ) );
 8         }
 9 
10         public static ISpecification< T > Or< T >( this ISpecification< T > leftSide, ISpecification< T > rightSide ) {
11             return new OrSpecification< T >( leftSide, rightSide );
12         }
13 
14         public static ISpecification< T > Or< T >( this ISpecification< T > left, Predicate< T > criteriaToSatisfy ) {
15             return left.Or( new Specification< T >( criteriaToSatisfy ) );
16         }
17 
18         private class AndSpecification< T > : ISpecification< T > {
19             public AndSpecification( ISpecification< T > leftCriteria, ISpecification< T > rightCriteria ) {
20                 this.leftCriteria = leftCriteria;
21                 this.rightCriteria = rightCriteria;
22             }
23 
24             public bool IsSatisfiedBy( T item ) {
25                 return leftCriteria.IsSatisfiedBy( item ) && rightCriteria.IsSatisfiedBy( item );
26             }
27 
28             private ISpecification< T > leftCriteria;
29             private ISpecification< T > rightCriteria;
30         }
31 
32         private class OrSpecification< T > : ISpecification< T > {
33             public OrSpecification( ISpecification< T > leftCriteria, ISpecification< T > rightCriteria ) {
34                 this.leftCriteria = leftCriteria;
35                 this.rightCriteria = rightCriteria;
36             }
37 
38             public bool IsSatisfiedBy( T item ) {
39                 return leftCriteria.IsSatisfiedBy( item ) || rightCriteria.IsSatisfiedBy( item );
40             }
41 
42             private ISpecification< T > leftCriteria;
43             private ISpecification< T > rightCriteria;
44         }
45     }

By accepting a Predicate delegate as the second argument you can now inline your lambdas and still take advantage of specifications. Client components can now take advantage of these extensions like this...

 1     public class SlipsRepository : ISlipsRepository {
 2         public SlipsRepository( ISlipDataMapper mapper ) {
 3             _mapper = mapper;
 4         }
 5 
 6         public IEnumerable< ISlip > AllAvailableSlips() {
 7             return _mapper.AllSlips( ).Where( Is.NotLeased( ) );
 8         }
 9 
10         public IEnumerable< ISlip > AllAvailableSlipsFor( IDock dockToFindSlipsOn ) {
11             return _mapper.AllSlips( ).Where( Is.NotLeased( ).And( Is.On( dockToFindSlipsOn ) ) );
12         }
13 
14         private readonly ISlipDataMapper _mapper;
15 
16         private static class Is {
17             public static ISpecification< ISlip > NotLeased() {
18                 return new Specification< ISlip >( slip => !slip.IsLeased( ) );
19             }
20 
21             public static Predicate< ISlip > On( IDock dock ) {
22                 return slip => dock.Equals( slip.Dock( ) );
23             }
24         }
25     }

The base specification class becomes a quick and easy...

 1     public class Specification< T > : ISpecification< T > {
 2         public Specification( Predicate< T > criteriaToSatisfy ) {
 3             _criteriaToSatisfy = criteriaToSatisfy;
 4         }
 5 
 6         public bool IsSatisfiedBy( T item ) {
 7             return _criteriaToSatisfy( item );
 8         }
 9 
10         private readonly Predicate< T > _criteriaToSatisfy;
11     }
comments powered by Disqus