Specification pattern revisited
Hi!
I’ve spoken about the specification pattern before. The first thing to note is that article is on the older 1.1 stuff, so no generics. However,in c# 3.5, we can crunch the specification pattern considerably. Here’s one idea..
First off the interface
[ Here's the code ] = [ignore this 8JKPNVRYP3CT ]
bool is_satisfied_by(T this_item);
}
Next the implemenation. Its not too different from the original
private readonly Predicate<T> specification_predicate_test;
public specification(Predicate<T>specification_predicate_test){
this.specification_predicate_test = specification_predicate_test;
}
public bool is_satisfied_by(T this_item){
return specification_predicate_test(this_item);
}
}
First off.. attach the interface. Then in the constructor give it the Predicate<T> (that’s the built in dot net predicate). In the “is_satisfied_by” method pass the item to the stored predicate and let it do the job.
That’s the specification done. Now to attend to the composite part.. the AND, OR combiniations…
Well, again, built in dot net to the rescue - extension methods..
public static specification<T> And<T> (this specification<T> first, Predicate<T> secondPredicate){
return new specification<T>(instance => first.is_satisfied_by(instance) && new specification<T>(secondPredicate).is_satisfied_by(instance));
}
public static specification<T> OR<T>(this specification<T> first, Predicate<T> secondPredicate){
return new specification<T>(instance => first.is_satisfied_by(instance) || new specification<T>(secondPredicate).is_satisfied_by(instance));
}
public static specification<T> Negate<T>(this specification<T> first){
return new specification<T>(instance => !first.is_satisfied_by(instance) );
}
}
Thats ok.. so how do we use it.. well. First define what specification. (our sample usage is bad invoices - pretty original
)
get{
var definition =
new specification<IInvoice>(x => x.due_date.CompareTo(DateTime.Now) > 0)
.And(x => x.total_amount > 1000)
.And(x => x.months_over_due > 3)
.OR(x => x.has_had_last_chance);
return definition;
}
}
And the sample usage. We have a bunch of invoices that we want to check..
var all_invoices_in_our_company = new List<IInvoice>();
all_invoices_in_our_company.Add(a_bad_invoice);
all_invoices_in_our_company.Add(a_bad_invoice);
all_invoices_in_our_company.Add(a_good_invoice);
all_invoices_in_our_company.Add(a_good_invoice);
all_invoices_in_our_company.Add(a_good_invoice);
all_invoices_in_our_company.Add(a_good_invoice);
all_invoices_in_our_company.Add(a_bad_invoice);
foreach (var invoice in all_invoices_in_our_company){
if (delinquent_invoice_specification.is_satisfied_by(invoice))
send(invoice).to_legal_department();
}
}
private IInvoice a_bad_invoice { get { return new Invoice(DateTime.Now.AddMonths(-10), 5000, 8, true); } }
private IInvoice a_good_invoice { get { return new Invoice(DateTime.Now.AddMonths(1), 1205, 0, false); } }
And there we have it.. crunched composite specification. [ Here's the code ]
Hope that helps
Zak