傳統做法需為每個業務規則建立類別(OddSpecification, PositiveSpecification...),並透過 AndSpecification, OrSpecification, NotSpecification 組合,代碼冗長。
Specification 核心只是 IsSatisfiedBy 邏輯,可用 Func<T,bool> 直接注入:
public class Specification<T> : ISpecification<T> {
private readonly Func<T, bool> _predicate;
public Specification(Func<T, bool> predicate) => _predicate = predicate;
public bool IsSatisfiedBy(T candidate) => _predicate(candidate);
}
public static class SpecificationExtensions {
public static ISpecification<T> And<T>(
this ISpecification<T> a, ISpecification<T> b) =>
new Specification<T>(c => a.IsSatisfiedBy(c) && b.IsSatisfiedBy(c));
public static ISpecification<T> Or<T>(...) => ... // 類似
public static ISpecification<T> Not<T>(...) => ... // 類似
}
public delegate bool Spec<T>(T candidate); // 或直接用 Func<T,bool>
public static class SpecExtensions {
public static Spec<T> And<T>(this Spec<T> a, Spec<T> b)
=> c => a(c) && b(c);
public static Spec<T> Or<T>(this Spec<T> a, Spec<T> b)
=> c => a(c) || b(c);
public static Spec<T> Not<T>(this Spec<T> a)
=> c => !a(c);
}
var odd = new Spec<int>(i => i % 2 == 1);
var oddAndPositive = odd.Or(i => i > 0); // 組合
foreach (var x in Enumerable.Range(-5, 10).Where(i => oddAndPositive(i)))
Console.WriteLine(x); // -5 -3 -1 1 2 3 4
| 維度 | 獨立類別 | 委托版本 |
|---|---|---|
| 邏輯內聚 | 高 | 分散 |
| 靜態可配置性 | 佳(可序列化型別名) | 差 |
| 編碼便利性 | 繁瑣 | 極簡 |
| 生命週期陷阱 | 明顯易察覺 | 隱蔽(閉包捕獲) |
委托版無法直接用於 LINQ to SQL,下篇將解決表達式樹組合問題。