Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
The Specification Pattern

Hot on the heels of my devastatingly fantastic post on an implementation of the Snapshot Pattern, I give you my next piece du resistance. In this little post, I'd like to delve into the Specification Pattern.

So what the heck is it? Matt Berther provided a pretty good introduction where he states:

It's primary use is to select a subset of objects based on some criteria...

That pretty much sums it up. What we want to do is extract out a specification for a subset of objects we might be interested in. We do this by creating specification objects.

You'll see something like this in a lot of applications:

This is fine in small doses, but your definition of a highly priced sale product might change over time, and we want to avoid having our logic for what IsOnSale and what a highly priced object actually is sprinkled throughout our code. One way to avoid this is to extract our logic into a Specification object like so:

Now the first loop I wrote can be written like so:

This is a slight improvement... There's actually more code to write, but now we can separately unit test each specification we create, without worrying about the loop:

Ok, so that's interesting, but we haven't even gone halfway, here. Why don't we refine that loop I wrote to use the new Generic collections in .NET 2.0:

Wow, now there's some serious savings on lines of code. "But you're missing the bit about the high priced products from the first example!?!" I hear you saying. Fear not, let's extract that into another specification like so:

We're still left with one problem, though. How do we tell the generic list of all products that we want the products that are both on sale and over a certain price? Let's try extracting our functionality into a Specification superclass first. This is what our ProductOnSaleSpecification and ProductPriceGreaterThanSpecification will inherit from. Once that's over with, we can create a CompositeSpecification, which is abstract, and allows us to pass in the left and right sides of a specification "equation." We can then implement yet another subclass (this time of CompositeSpecification) that we'll call AndSpecification. Here it is:

Now our original loop that looks for highly priced products that are on sale looks like this:

We're getting there, but we're still not done. The code we just wrote is soooo .NET 1.1. Let's get fluent with our interfaces and add some sweet sugar to our Specification base class...

I've just added some convenience methods to Specification that will let us chain together any specifications we create. Therefore, our original loop ascends to a new level of sexiness...

For the "slow" ones in the class I've put up a pretty picture for you to look at, while the rest of the class downloads the code, complete with unit tests.

kick it on DotNetKicks.com


Posted 12-13-2006 7:34 PM by Jeff Perrin
Filed under: , ,

[Advertisement]

Comments

Joey Beninghove wrote re: The Specification Pattern
on 12-13-2006 10:45 PM

Ahh, yes.  The specification patter is proving very useful for me these days as well.  And the syntax you were able to create with the composite specification is very elegant.  Nice to see someone else really care about code craftsmanship.  

I also like to use simple specifications for reusable bits of validation logic as well.  Like validating empty strings, dates, string lengths, numerics and any other validation I have to do in more than one place (DRY).  

Looking forward to getting my Nilsson DDD book next week...  :)

Rob Eisenberg wrote re: The Specification Pattern
on 12-14-2006 9:23 AM

Jeff,

Nice post.  I particularly liked the fluent interface addition at the end.

Jeff Perrin wrote re: The Specification Pattern
on 12-14-2006 11:45 AM

Thanks, guys.  To be fair, the fluent interface stuff is nothing new... we've been using a Java version of this code at work for several months now, and I've seen ruby versions as well.  All I've done is convert the pattern to C# utilizing all the new .NET 2.0 stuff (generics, collections accepting predicates, etc).

Billy McCafferty wrote re: The Specification Pattern
on 12-14-2006 12:31 PM

Nice summary Jeff...you'd probably enjoy taking a look at my 2nd refactoring challenge:  http://devlicio.us/blogs/billy_mccafferty/archive/2006/11/23/refactor-it-challenge-2.aspx.  The main challenge was to refactor the code to the specification pattern.  The specification/interpreter pattern is definitely one of my favorites in the realm of useful patterns.

Jaakko Haapasalo wrote re: The Specification Pattern
on 12-14-2006 12:36 PM

As an aside, the specification classes are called predicates in C++. This is a pretty common pattern in the C++ STL. Composing the specifications is neat, although wouldn't it be nice if we didn't have to implement propositional logic operators yet again...

Jeff Perrin wrote re: The Specification Pattern
on 12-14-2006 3:42 PM

Jaakko,

They're still "called" predicates in C#...

http://msdn2.microsoft.com/en-us/library/bfcke1bz.aspx

Basically, the IsSatisfiedBy method is the predicate in this instance. What the heck does "implement propositional logic operators yet again" mean? I'm curious.

someone wrote re: The Specification Pattern
on 12-14-2006 8:29 PM

How is this different than using the predicate delegate and anonymous delegates?:

productList.FilterBy(delegate(Product p) {

   return p.Price > 100.0;

});

If you want to reuse them just place them on a static class.

static class ProductUtilities

{

   static bool IsExpensive(Product p) { return p.Price > 100.0; }

   ...

}

And they can also be chained:

productList.FilterBy(delegate(Product p) {

   return ProductUtilities.IsExpensive(p) &&

       ProductUtilities.IsOnSale(p);

});

In comparison your code feels soo Java-like.

Matt Berther wrote re: The Specification Pattern
on 12-14-2006 9:16 PM

Jeff,

Thanks for the link.

I really like what you've done with this pattern, especially the fluent interface at the end. I will definately have to find a way to work this into my own stuff.

Jeff Perrin wrote re: The Specification Pattern
on 12-15-2006 10:26 AM

Someone,

I wrote about the exact method you specify here:

http://www.jeffperrin.com/index.php/2006/06/28/specification-pattern-with-predicates/

After mulling it over for a while, I realized I hated the idea of having a static class full of specification methods. You're right that the code from this article looks "Java-like" (except for the fact that we're passing a freaking method to an object), but the alternative is the messy looking anonymous delegate syntax in C# 2.0. When 3.0 arrives, *then* we'll talk. ;)

TrackBack wrote http://dotnetkicks.com/patterns/the_specification_pattern
on 12-16-2006 4:56 PM
Gary wrote re: The Specification Pattern
on 12-18-2006 8:57 PM

I've been using the Specification pattern for a while now (very similar to what you have) and it works a treat [cuts the amount of code I have to write.. always a good thing!!!].

Just a couple of differences between what we have:

1) NotSpecification<T> is derived directly from Specification<T> and not CompositeSpecification<T>, I just use :

public bool IsSatisifedBy(T candidate)

{

return !m_wrapped.IsSatisfiedBy(candidate);

}

2) On the abstract Specification I expose a method:

public IList<T> Filter(IList<T> candidates)

{

return new List<T>(candidates).FindAll(isSatisfiedBy);

}

(whether this is a good or a bad decision, I will let you decide)

Jeff Perrin :: Jump the Fence or Walk Around » Blog Archive » What I Learned From X That Makes Me a Better Programmer in Y wrote Jeff Perrin :: Jump the Fence or Walk Around &raquo; Blog Archive &raquo; What I Learned From X That Makes Me a Better Programmer in Y
on 10-03-2007 11:46 PM

Pingback from  Jeff Perrin :: Jump the Fence or Walk Around  &raquo; Blog Archive   &raquo; What I Learned From X That Makes Me a Better Programmer in Y

Reshef’s tip of the day » Blog Archive » The Specification Pattern wrote Reshef&#8217;s tip of the day &raquo; Blog Archive &raquo; The Specification Pattern
on 12-05-2007 3:38 AM

Pingback from  Reshef&#8217;s tip of the day  &raquo; Blog Archive   &raquo; The Specification Pattern

Your Daily Mark.Net wrote Specification pattern
on 01-11-2008 2:02 AM

The last couple of days I&#39;m learning a lot about the specification pattern. These links can helped

Specification Pattern and Lambdas « Ang3lFir3 - Life as a Code Poet wrote Specification Pattern and Lambdas &laquo; Ang3lFir3 - Life as a Code Poet
on 07-29-2008 1:51 AM

Pingback from  Specification Pattern and Lambdas &laquo; Ang3lFir3 - Life as a Code Poet

specification pattern for selection on lists at dpdk Open Source wrote specification pattern for selection on lists at dpdk Open Source
on 09-10-2008 11:03 AM

Pingback from  specification pattern for selection on lists at dpdk Open Source

Jeff Perrin :: JeffPerrin.com » Blog Archive » What I Learned From X That Makes Me a Better Programmer in Y wrote Jeff Perrin :: JeffPerrin.com &raquo; Blog Archive &raquo; What I Learned From X That Makes Me a Better Programmer in Y
on 10-12-2008 10:21 AM

Pingback from  Jeff Perrin :: JeffPerrin.com  &raquo; Blog Archive   &raquo; What I Learned From X That Makes Me a Better Programmer in Y

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)