程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 趣味編程:C#中Specification模式的實現(參考答案 - 上)

趣味編程:C#中Specification模式的實現(參考答案 - 上)

編輯:關於C#

Specification模式的作用是構建可以自由組裝的業務邏輯元素。不過就上篇文章的示例來看,“標准”的Specification模式的實現還是比較麻煩的,簡單的功能也需要較復雜的代碼。不過,既然說是“標准”的方式,自然就是指可以在任意面向對象語言中使用的實現方式,不過我們使用的是C#,在實際開發過程中,我們可以利用C#如今的強大特性來實現出更容易使用,更輕量級的 Specification模式。

當然,有利也有弊,在使用“標准”還是“輕量級”的問題上,還要根據你的需求來進行選擇。

Specification模式的關鍵在於,Specification類有一個IsSatisifiedBy函數,用於校驗某個對象是否滿足該 Specification所表示的條件。多個Specification對象可以組裝起來,並生成新Specification對象,這便可以形成高度可定制的業務邏輯。從中可以看出,一個Specification對象的關鍵,其實就是一個IsSatisifiedBy方法的邏輯。每種對象,一段邏輯。每個對象的唯一關鍵,也就是這麼一段邏輯。因此,我們完全可以構造這麼一個“通用”的類型,允許外界將這段邏輯通過構造函數“注入”到 Specification對象中:

public class Specification<T> : ISpecification<T>
{
  private Func<T, bool> m_isSatisfiedBy;

  public Specification(Func<T, bool> isSatisfiedBy)
  {
    this.m_isSatisfiedBy = isSatisfiedBy;
  }

  public bool IsSatisfiedBy(T candidate)
  {
    return this.m_isSatisfiedBy(candidate);
  }
}

嗯嗯,這也是一種依賴注入。在普通的面向對象語言中,承載一段邏輯的最小單元只能是“類”,只是我們說,某某類中的某某方法就是我們需要的邏輯。而在C#中,從最早開始就有“委托”這個東西可用來承載一段邏輯。與其為每種情況定義一個特定的Specification類,讓那個 Spcification類去訪問外部資源(即建立依賴),不如我們將這個類中唯一需要的邏輯給准備好,各種依賴直接通過委托由編譯器自動保留,然後直接注入到一個“通用”的類中。很關鍵的是,這樣在編程方面也非常容易。

至於原本ISpecification<T>中的And,Or,Not方法,我們可以將它們提取成擴展方法。有朋友說,既然有了擴展方法,那麼對於一些不需要訪問私有成員/狀態的方法,都應該提取到實體的外部,避免“污染”實體。不過我不同意,在我看來,到底是用實例方法還是擴展方法,還是個根據職責和概念而一定的。我在這裡打算使用擴展的目的,是因為And,Or,Not並非是一個Specification對象的邏輯,並不是一個Specification對象說,“我要去And另一個”,“我要去Or另一個”,或者“我要造……取反”。就好比二元運算符&&、||、或者+、-,左右兩邊的運算數字有主次之分嗎?沒有,它們是並列的。因此,我選擇使用額外的擴展方法,而不是將這些職責交給某個Specification對象:

public static class SpecificationExtensions 
{
  public static ISpecification<T> And<T>(
    this ISpecification<T> one, ISpecification<T> other)
  {
    return new Specification<T>(candidate =>
      one.IsSatisfiedBy(candidate) && other.IsSatisfiedBy(candidate));
  }

  public static ISpecification<T> Or<T>(
    this ISpecification<T> one, ISpecification<T> other)
  {
    return new Specification<T>(candidate =>
      one.IsSatisfiedBy(candidate) || other.IsSatisfiedBy(candidate));
  }

  public static ISpecification<T> Not<T>(this ISpecification<T> one)
  {
    return new Specification<T>(candidate => !one.IsSatisfiedBy(candidate));
  }
}

此外,使用擴展方法的好處在於,如果我們想要加一個邏輯運算(如“異或”),那麼是不需要修改接口的。修改接口是一件勞民傷財的事情。

至此,我們使用Specification對象就容易多了,因為不需要為每段邏輯創建一個獨立的 ISpecification<T>類型。但是,其實還有更簡單的:直接使用委托。既然整個Specificaiton對象的邏輯可以使用一個委托直接表示,那為什麼我們還需要一個“外殼”呢?不如直接使用這樣的委托類型:

public delegate bool Spec<T>(T candicate);

當然,您也可以直接使用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使用,又該怎麼做呢?我們的“下”便會設法解決這個問題。

文章來源: http://www.cnblogs.com/JeffreyZhao/archive/2009/09/28/specification-pattern-in-csharp-practice-answer-1.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved