Sunday, April 30, 2006

Specification Objects

Domain Driven Design by Eric Evans has several patterns mixed in it, among the ideas about how to leverage the domain in development.

One of the patterns is the Specification pattern. For me, it's primary benefit has been to reduce code duplication when you're using collections, and need to find subsets of the objects in those collections, depending on various Criteria.

For our project we have this problem in spades, with contracts and measurement points.

Sometimes we want to find all Sales contracts. Sometime we want to find an Oil Contract. Sometimes we want to find Oil contracts that are also Sales contracts. We have many many dimension on our contracts, and need to slice and dice them many different ways.

Our original implementation looked like this:

findAllSalesContracts
List results = new List
foreach contract in getContracts()
if contract.isSales
results.add(contract)
return results

findAllOilContracts
List results = new List

foreach contract in getContracts()
if contract.isOil
results.add(contract)
return results


findAllOilSalesContracts
List results = new List
foreach contract in findAllOilContracts()
if contract.isOil
results.add(contract)
return results

See the duplication, the repeated code? Imagine 4 or 5 conditions. This gets unmanagable.

Now here's a Specification Implementation:

class OilSpecification
isSatisfiedBy aContract
return aContract.isOil

class SalesSpecification
isSatisfiedBy aContract
return aContract.isSales


class ContractFinder
findAll(Collection, Specification)
List results = new List
foreach element in collection
if Specification.isSatisfiedBy(element)
results.add(element)
return results

findAllSalesContracts
return ContractFinder.findAll(getContracts, SalesSpecification)

findAllOilContractsList
return ContractFinder.findAll(getContracts, OilSpecification)

findAllOilSalesContracts
return ContractFinder.findAll(getContracts, SalesSpecification.and(OilSpecification))

Removed several hundred lines of code from our application.

2 Comments:

Blogger Vladimir Levin said...

You mentioned that performance could be improved with this approach, but I'm not sure how - since this approach involves asking the database for all the records and then filtering them usig the specs. Using toplink expressions, one can take advantage of indexes on the database tables and the syntax is quite similar to specs, so one gets the same kinds of benefits in terms of code duplication. I'd love to see a posting about performance tuning these spec objects.

8:45 p.m.  
Blogger Ted O'Grady said...

The primary performance gain (about 5% for our tests). Is the reduction in the number of loops. For some of our common queries we would loop over the set of measurement points multiple times (up to 5!)

9:58 p.m.  

Post a Comment

<< Home