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

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

編輯:關於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);

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