首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

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

     編輯:關於C語言

Specification 模式的作用是構建可自由組裝的業務邏輯元素。但就上篇文章的範例來看,「標準」的 Specification 模式實現仍顯繁瑣,簡單功能也需要較複雜的程式碼。不過,既然說是「標準」方式,自然是指可在任意面向物件語言中使用的實現。但我們使用的是 C#,在實際開發中,可以善用 C# 強大特性實現更易用、更輕量的 Specification 模式。

當然,有利也有弊,在選擇「標準」還是「輕量級」時,仍需根據需求決定。

Specification 模式的關鍵在於:Specification 類別有一個 IsSatisfiedBy 方法,用於驗證物件是否符合條件。多個 Specification 物件可組裝生成新物件,形成高度可客製化的業務邏輯。由此可見,一個 Specification 物件的核心其實就是一個 IsSatisfiedBy 方法的邏輯。每種對象、一段邏輯、每個對象的唯一關鍵就是這段邏輯。因此,我們完全可以構造一個「通用」類型,允許外界透過建構函數將這段邏輯「注入」到 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 類別,讓該類別去訪問外部資源(建立依賴),不如將類別中唯一需要的邏輯準備好,各種依賴直接透過委託由編譯器自動保留,再注入到一個「通用」類別中。關鍵是這樣在程式設計上也非常容易。

至於原本 ISpecification<T> 中的 And、Or、Not 方法,我們可以將它們提取成擴充方法。有朋友說,既然有擴充方法,對於不需要訪問私有成員/狀態的方法,都應提取到實體外部,避免「污染」實體。但我不同意,在我看來,到底用實例方法還是擴充方法,取決於職責與概念。我在這裡使用擴充方法,是因為 And、Or、Not 並非某個 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> 類型。但其實還有更簡單的:直接使用委託。既然整個 Specification 物件的邏輯可用一個委託直接表示,為何還需要「外殼」?不如直接使用這樣的委託類型:

public delegate bool Spec<T>(T candidate);
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved