Friday, December 28, 2007

Fluent Query Language Through Strategy Composition

In the last post we created a Fluent API that builds a domain object hierarchy. In this post we are going to create a Fluent API that acts as a domain specific querying language for this same domain. Let's take a look at a slightly modified example from a previous post:
CustomerQuery query = questionThatAsks.whichCustomers(bought(anything(), between("01/01/2006", "01/01/2007")));
List<Customer> customers = query.ask(listOfAllCustomers);

This example creates a CustomerQuery object that is capable of answering the question that has been asked, 'Which customers bought anything between 01/01/2006 and 01/01/2007?'. We could have picked any date or date range. We could have specified that are looking for Customers that bought compact discs. Pretty cool, huh? Can you imagine how it works?

We are doing a combination of two things to get this affect. First we are gathering information about the desired return type base on which builder method we call (ie. whichCustomers, howManyCustomers, haveAnyCustomers, etc.). When we ask 'whichCustomers' we know that we are going to return a query object of type <List<Customer>>. If we ask 'howManyCustomers' we know we should return an int.

Secondly we are using a composing the behavior of our query using a Strategy Pattern. In this case our strategy takes the form of a Predicate. You could expand on your interface with different types of Strategy objects. For instance, you could add a Strategy to do proper formatting base on which builder method you call. Or a Strategy to add select a particular property off of the Customer objects.

You don't even have to even create the intermediate CustomerQuery object. You would just as easily get your answer directly. Take some time to look over the full example source. Then think about your business domain and how a similar pattern could be used to allow you to code in your business logic in this incredibly expressive way.

No comments: