趣味編程:C#中Specification模式的實現(xiàn)
2009-09-15 20:15 Jeffrey Zhao 閱讀(15298) 評論(56) 收藏 舉報今天有朋友在問了我這么一個問題:怎么實現(xiàn)OrWhere的功能?我猜測,他的意思是要實現(xiàn)這樣的功能:
static IEnumerable<int> MorePredicate(IEnumerable<int> source) { return source.OrWhere(i => i > 0); // 或所有的正數(shù) } static void Main(string[] args) { var array = Enumerable.Range(-5, 10).ToArray(); var odd = array.Where(i => i % 2 != 0); // 排除所有偶數(shù) var oddOrPositive = MorePredicate(odd); foreach (var i in oddOrPositive) { Console.WriteLine(i); } }
以上代碼應(yīng)該輸出-5,-3,-1,1,2,3,4。但顯然,這段代碼是無法編譯通過,無法通過補充類庫來實現(xiàn)的。因為在Main方法中的Where過后,已經(jīng)排除了所有的偶數(shù),于是在接下來的代碼中是無法從新的序列中再次恢復(fù)元素的。
不過這個要求讓我想起了Specification模式。Specification模式的作用是構(gòu)建可以自由組裝的業(yè)務(wù)邏輯元素。Specification類有一個IsSatisifiedBy函數(shù),用于校驗?zāi)硞€對象是否滿足該Specification所表示的條件。多個Specification對象可以組裝起來,并生成新Specification對象,這便可以形成高度可定制的業(yè)務(wù)邏輯。例如,我們可以使用依賴注入(控制反轉(zhuǎn))的方式來配置這個業(yè)務(wù)邏輯,以此保證系統(tǒng)的靈活性。
如果您點開上面那個Wikipedia的鏈接,就會發(fā)現(xiàn)這段描述大約是一個英譯中的練習(xí)。抄完了“定義”,再來看看同樣引自Wikipedia的UML圖:
一般來說,Specification模式則意味著實現(xiàn)這樣一個接口:
public interface ISpecification<T> { bool IsSatisfiedBy(T candidate); ISpecification<T> And(ISpecification<T> other); ISpecification<T> Or(ISpecification<T> other); ISpecification<T> Not(); }
實現(xiàn)了ISpecification的對象則意味著是一個Specification,它可以通過與其他Specification對象的And,Or或?qū)ψ陨淼娜》磥砩尚碌倪壿?。為了方便這些“組合邏輯”的開發(fā),我們還會準(zhǔn)備一個抽象的CompositeSpecification類:
public abstract class CompositeSpecification<T> : ISpecification<T> { public abstract bool IsSatisfiedBy(T candidate); public ISpecification<T> And(ISpecification<T> other) { return new AndSpecification<T>(this, other); } public ISpecification<T> Or(ISpecification<T> other) { return new OrSpecification<T>(this, other); } public ISpecification<T> Not() { return new NotSpecification<T>(this); } }
CompositeSpecification提供了構(gòu)建復(fù)合Specification的基礎(chǔ)邏輯,它提供了And、Or和Not方法的實現(xiàn),讓其他Specification類只需要專注于IsSatisfiedBy方法的實現(xiàn)即可。例如,以下便是三種邏輯組合的具體實現(xiàn):
public class AndSpecification<T> : CompositeSpecification<T> { private ISpecification<T> m_one; private ISpecification<T> m_other; public AndSpecification(ISpecification<T> x, ISpecification<T> y) { m_one = x; m_other = y; } public override bool IsSatisfiedBy(T candidate) { return m_one.IsSatisfiedBy(candidate) && m_other.IsSatisfiedBy(candidate); } } public class OrSpecification<T> : CompositeSpecification<T> { private ISpecification<T> m_one; private ISpecification<T> m_other; public OrSpecification(ISpecification<T> x, ISpecification<T> y) { m_one = x; m_other = y; } public override bool IsSatisfiedBy(T candidate) { return m_one.IsSatisfiedBy(candidate) || m_other.IsSatisfiedBy(candidate); } } public class NotSpecification<T> : CompositeSpecification<T> { private ISpecification<T> m_wrapped; public NotSpecification(ISpecification<T> x) { m_wrapped = x; } public override bool IsSatisfiedBy(T candidate) { return !m_wrapped.IsSatisfiedBy(candidate); } }
于是,我們便可以使用Specification模式來處理剛才那位朋友的問題。例如,首先他需要排除所有的偶數(shù),那么我們不妨實現(xiàn)一個OddSpecification:
public class OddSpecification : CompositeSpecification<int> { public override bool IsSatisfiedBy(int candidate) { return candidate % 2 != 0; } }
自然,還有用于獲得所有正數(shù)的Specification類:
public class PositiveSpecification : CompositeSpecification<int> { public override bool IsSatisfiedBy(int candidate) { return candidate > 0; } }
于是在使用時,我們會將其通過Or方法組合起來:
static ISpecification<int> MorePredicate(ISpecification<int> original) { return original.Or(new PositiveSpecification()); } static void Main(string[] args) { var array = Enumerable.Range(-5, 10).ToArray(); var oddSpec = new OddSpecification(); var oddAndPositiveSpec = MorePredicate(oddSpec); foreach (var item in array.Where(i => oddAndPositiveSpec.IsSatisfiedBy(i))) { Console.WriteLine(item); } }
但是,您覺得這個做法怎么樣?我覺得過于殺雞用牛刀,高射炮打蚊子了。Specification模式雖然常用,但是用在這里太重量級了。如果我們?yōu)槊恳粋€函數(shù)都補充一個Specification類,至少會讓我感到厭倦。
以上的代碼其實轉(zhuǎn)載自Wikipedia詞條,不過我修改了一些命名,以及改寫成泛型版本。我們有理由推測,Wikipedia上是非常舊的內(nèi)容,很可能是在C#只是1.0版本的時候編寫的代碼(或者說它為了“兼容”Java那種語言的實現(xiàn)方式)。那么在實際開發(fā)過程中,我們又該如何利用C#如今的強(qiáng)大特性來實現(xiàn)出更容易使用,甚至是更為“輕量級”的Specification模式呢?
此外,剛才又收到那位朋友的消息,他其實是想在使用LINQ to SQL時實現(xiàn)“可擴(kuò)展的邏輯”。那么,對于他說的情況,我們又該如何應(yīng)對呢?
浙公網(wǎng)安備 33010602011771號