當然,您也可以直接使用Func<T, bool>。我在這裡創建Spec的目的,是因為我想“明確”這裡其實是一個Specification,而不是一個普通的“接受T作為參數,返回 bool的方法”。於是現在,我們便可以用這樣的擴展方法來編寫And,Or和Not:
public static class SpecExtensions
{
public static Spec<T> And<T>(this Spec<T> one, Spec<T> other)
{
return candidate => one(candidate) && other(candidate);
}
public static Spec<T> Or<T>(this Spec<T> one, Spec<T> other)
{
return candidate => one(candidate) || other(candidate);
}
public static Spec<T> Not<T>(this Spec<T> one)
{
return candidate => !one(candidate);
}
}
用它來編寫上次的示例便容易多了:
static Spec<int> MorePredicate(Spec<int> original)
{
return original.Or(i => i > 0);
}
static void Main(string[] args)
{
var array = Enumerable.Range(-5, 10).ToArray();
var oddSpec = new Spec<int>(i => i % 2 == 1);
var oddAndPositiveSpec = MorePredicate(oddSpec);
foreach (var item in array.Where(i => oddAndPositiveSpec(i)))
{
Console.WriteLine(item);
}
}
由於有C#的擴展方法和委托,在C#中使用Specification模式比之前要容易許多。不過,在某些時候,我們可能還是需要老老實實按照標准來做。創建獨立的Specification對象的好處是在一個單獨的地方內聚地封裝了一段邏輯,因此適合較集中,較“重”的邏輯,而“委托”則適合輕便的實現。委托的另一個優勢是使用方便,但它的缺點便是難以“靜態表示”。如果您在使用Specification模式時,需要根據外部配置來決定進行何種組裝,那麼可能只有為每種邏輯創建獨立的Specification對象了。此外,使用委托還有一個“小缺點”,即它可能會“不自覺”地提升對象的生命周期,可能會形成一些延遲方面的陷阱。
當然,我並不是說獨立Specification對象就不會造成生命周期延長——只要功能實現一樣,各方面也應該是相同的。只不過獨立的Specificaiton對象給人一種“正式”而“隆重”的感覺,容易讓人警覺,因而緩解了這方面問題。
不過還有一個問題我們還沒有解決——我們現在組裝的是委托或Specification對象,但如果我們需要組裝一個表達式樹,組裝完畢後交給如LINQ to SQL使用,又該怎麼做呢?我們的“下”便會設法解決這個問題。