Comparison of Specification Pattern, Func<T,bool> Predicates and Pipes & Filters
M

2

7

I'm doing some R&D work, and as such am exploring design patterns. I have recently been reading up on the Specification pattern and was referred to this great article.

I was intrigued by the simplicity and cleanliness of the code, but i started to draw some comparisons to implementing the same cleanliness using other techniques.

Consider the following interface contract for a service layer:

public interface IFooDataService
{
   ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification);
   ICollection<Foo> GetFooByPredicate(Func<Foo,bool> predicate);
   ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs);
}

So, some initial points:

  • All three return a collection of Foo objects
  • All three take one single argument
  • Specification method restricts access to specific requirements
  • Predicate method has basically no restriction
  • Search args method restricts access to specific requirements

Now, onto the implementation:

public ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification)
{
    return fooDataRepository
            .Find()
            .Where(f => specification.IsSatisfiedBy(f))
            .ToList();
}

public ICollection<Foo> GetFooByPredicate(Func<Foo, bool> predicate)
{
    return fooDataRepository
            .Find()
            .Where(predicate)
            .ToList();
}

public ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs)
{
    return fooDataRepository
            .Find()
            .WhereMeetsSearchCriteria(searchArgs)
            .ToList();
}

Points on the implementation:

  • All three are extremely simple in implementation (one line of chained code)
  • Specification and Search Args filtered implemented externally.
  • Search args method simply uses IEnumerable extension method to inspect args

So, that being said, under what conditions would you use one of above 3 techniques?

My thoughts on Specification Pattern:

  • Nice in that it isolates business/domain requirements into reusable components
  • Extremely easy to read, makes code speak english
  • Fair bit of code involved (interfaces, abstract classes). If i were to use this, i would put the abstractions in a common assembly (so i dont have a bunch of static files in my solution).
  • Easy to change requirements by only changing specification, and not service layer.
  • Supreme testability of domain logic (specifications)

My thoughts on Extension Methods (Pipes & Filters):

  • 'Weighty' in logic, but still result in the same simplicity.
  • Isolate query logic from service layer to static methods
  • Still requires "reflection" of sort (inspecting supplied search args and building up query)
  • Allows you to code architecture (repository, service layer) first, without thinking about specific business requirements (which is handy in certain scenarios)

My thoughts on Predicate Method:

  • Could be used where you need coarse grained control over the queries.
  • Good for small projects, where specifications may be overdoing it

My final thought logic is that if you are working on a complex business application where business requirements are known up front but may change over time, then i would use Specification pattern.

But for a application that is a "startup", ie requirements will evolve over time and has a multitude of ways to retrieve data without complex validation, i would use the Pipes and Filters methods.

What are your thoughts? Have any of you run into problems with any of the above methods? Any recommendations?

About to start a new project so these types of considerations are critical.

Thanks for the help.

EDIT for Clarification on Specification pattern

Here is same usage of the Specification pattern.

Specification<Foo> someSpec; // Specification is an abstract class, implementing ISpecification<TEntity> members (And, Or, Not, IsSatisfiedBy).
someSpec = new AllFoosMustHaveABarSpecification(); // Simple class which inherits from Specification<Foo> class, overriding abstract method "IsSatisfiedBy" - which provides the actual business logic.    
ICollection<Foo> foos = fooDataService.GetFoosBySpecification(someSpec);
Munch answered 26/8, 2010 at 23:51 Comment(6)
It's not entirely clear how Specification<Foo> is different from Func<Foo, bool>.Wince
@Wince - i didnt want to post code about how the specification pattern is implemented, as a) its a lot of code (4 classes) and b) the link i provided with provide the implementation detail.Munch
I read the link; I just don't see the semantic difference.Wince
@Gabe, then you obviously don't understand the Specification pattern. Func<Foo,bool> means you pass in a lamba predicate - ie .GetFoosByPredicate(f => f.Bar = "FooBar"); Specification is a specific object which the satsifying properties encapsulates in the method (IsSatisfiedBy).Munch
So you can write .GetFoosByPredicate(f => f.Bar = "FooBar") or .GetFoosBySpecification(new Specification<Foo>(f => f.Bar = "FooBar"))? I don't see what that buys you. Does one way enable any more expressiveness than the other? Is one easier to use? Faster? Provide better abstraction?Wince
.GetFoosBySpecification(new Specification<Foo>(f => f.Bar = "FooBar")) is an invalid statement. Specification is an abstract class and thus cannot be instantiated, you must use of the derived types ie "AllFoosMustHaveABarSpecification". It's obvious you didnt read the entire link, so i dont know why we're arguing here.Munch
G
2

From my little experience :

  1. User requirement always change and i don't know why my boss always allows that changes to come. So +1 to Specification
  2. Programmer here is more like "manual worker" than "knowledge worker". You know.. the one who types all day long. By using Specification, i can ensure myself that everybody "types". This is supported by the nature of my project. It needs many many different implementation for same purpose. Don't ask me why.
  3. Use a design pattern which gives you highest modularity and flexibility and of course testability. Here's a little story.
    In a one fine day, my mate told me that he wrote a class which enabled us to calculate 32 kinds of way of calculating X. And he already implemented all that. Hoho, that was such a heroic programming i think. He spent several weeks doing that in the middle of the night. He believed that he was a good programmer so he insisted everybody to use his masterpiece.
    We, at that time, didn't care about unit testing so we used his masterpiece. What happened then? The code crashed all the time. Well, from that time i realized how important unit test and modularity are.
Gerfalcon answered 27/8, 2010 at 2:42 Comment(0)
K
0

Well, first I'd write the Predicate method, even if it's only used as a private implementation detail for the other two:

private ICollection<Foo> GetFoosBySpecification(Specification<Foo> spec) 
{ 
    return GetFooByPredicate(f => spec.IsSatisfiedBy(f));
} 

The search argument function would be a similar one-liner.

Beyond that, I really can't say anything in the abstract. I'd have to know more about the data structure to decide the best way to seek them.

Koehn answered 27/8, 2010 at 2:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.