您好,登錄后才能下訂單哦!
規范模式是一種特定的軟件設計模式,通過使用布爾邏輯 (維基百科)將業務規則鏈接在一起,可以重新組合業務規則。
在實際中,它主要用于 為實體或其他業務對象定義可重用的過濾器。
在本節中,我們將看到需要規格模式。本節是通用的,與ABP的實現無關。
假設您有一種服務方法來計算客戶的總數,如下所示:
public class CustomerManager { public int GetCustomerCount() { // TODO ... return 0 ; } }
您可能希望通過過濾器獲得客戶數量。例如,您可能會有高級客戶(其余額超過10萬美元),或者您可能想要通過 注冊年度過濾客戶。然后,您可以創建其他方法,如GetPremiumCustomerCount(), GetCustomerCountRegisteredInYear(int year), GetPremiumCustomerCountRegisteredInYear(int year)等。由于您有更多的標準,因此無法為每種可能性創建組合。
這個問題的一個解決方案是規范模式。我們可以創建一個獲取參數作為過濾器的方法:
public class CustomerManager { private readonly IRepository<Customer> _customerRepository; public CustomerManager(IRepository<Customer> customerRepository) { _customerRepository = customerRepository; } public int GetCustomerCount(ISpecification<Customer> spec) { var customers = _customerRepository.GetAllList(); var customerCount = 0; foreach (var customer in customers) { if (spec.IsSatisfiedBy(customer)) { customerCount++; } } return customerCount; } }
因此,我們可以獲得任何對象作為實現 ISpecification <Customer>接口的參數,定義如下:
public interface ISpecification<T>{ bool IsSatisfiedBy(T obj); }
我們可以與客戶一起致電IsSatisfiedBy,以測試該客戶是否有意。因此,我們可以使用相同的GetCustomerCount與不同的過濾器,而不改變方法本身。
雖然這個解決方案在理論上是相當不錯的,但應該改進,以更好地在C#中工作。例如,它是沒有效率得到所有客戶提供從數據庫來檢查它們是否滿足給定的規格/條件。在下一節中,我們將看到ABP的實現,克服了這個問題。
ABP定義了ISpecification界面,如下所示:
public interface ISpecification<T>{ bool IsSatisfiedBy(T obj); Expression<Func<T, bool>> ToExpression(); }
添加ToExpression()方法,該方法返回一個表達式,并用于更好地與IQueryable和 Expression樹的集成。因此,我們可以輕松地將規范傳遞給存儲庫,以在數據庫級別應用過濾器。
我們通常從規范<T>類繼承,而不是直接實現ISpecification <T>接口。規范類自動實現IsSatisfiedBy方法。所以,我們只需要定義ToExpression。我們來創建一些規范類:
//Customers with $100,000+ balance are assumed as PREMIUM customers.public class PremiumCustomerSpecification : Specification<Customer>{ public override Expression<Func<Customer, bool>> ToExpression() { return (customer) => (customer.Balance >= 100000); } }//A parametric specification example.public class CustomerRegistrationYearSpecification : Specification<Customer>{ public int Year { get; } public CustomerRegistrationYearSpecification(int year) { Year = year; } public override Expression<Func<Customer, bool>> ToExpression() { return (customer) => (customer.CreationYear == Year); } }
如你所見,我們只是實現了簡單的lambda表達式 來定義規范。讓我們使用這些規格來獲得客戶數量:
count = customerManager.GetCustomerCount(new PremiumCustomerSpecification()); count = customerManager.GetCustomerCount(new CustomerRegistrationYearSpecification(2017));
現在,我們可以優化 CustomerManager 在數據庫中應用過濾器:
public class CustomerManager { private readonly IRepository<Customer> _customerRepository; public CustomerManager(IRepository<Customer> customerRepository) { _customerRepository = customerRepository; } public int GetCustomerCount(ISpecification<Customer> spec) { return _customerRepository.Count(spec.ToExpression()); } }
這很簡單 我們可以將任何規范傳遞給存儲庫,因為 存儲庫可以使用表達式作為過濾器。在此示例中,CustomerManager是不必要的,因為我們可以直接使用具有規范的存儲庫來查詢數據庫。但是認為我們想對一些客戶執行業務操作。在這種情況下,我們可以使用具有域服務的規范來指定客戶進行工作。
規格一個強大的功能是,它們可組合使用 AND,OR,不和ANDNOT擴展方法。例:
var count = customerManager.GetCustomerCount(new PremiumCustomerSpecification().And(new CustomerRegistrationYearSpecification(2017)));
我們甚至可以從現有規范中創建一個新的規范類:
public class NewPremiumCustomersSpecification : AndSpecification<Customer>{ public NewPremiumCustomersSpecification() : base(new PremiumCustomerSpecification(), new CustomerRegistrationYearSpecification(2017)) { } }
規范是Specification 類的一個子類,只有在兩個規范都滿足的時候才能滿足。那么我們可以像其他規格一樣使用NewPremiumCustomersSpecification:
var count = customerManager.GetCustomerCount(new NewPremiumCustomersSpecification());
雖然規范模式比C#lambda表達式更早,但它通常與表達式進行比較。一些開發者可能會認為它不再需要,我們可以直接將表達式傳遞到存儲庫或域服務,如下所示:
var count = _customerRepository.Count(c => c.Balance > 100000 && c.CreationYear == 2017);
由于ABP的存儲庫支持expessions,這是完全有效的用法。您不必在應用程序中定義或使用任何規范,您可以使用表達式。那么說明什么呢?為什么和何時應該考慮使用它們?
使用規格的一些好處:
Reusabe:認為您需要在您的代碼庫中的許多地方使用PremiumCustomer過濾器。如果您使用表達式而不是創建規范,如果您以后更改“高級客戶”定義(例如,要將最終余額從100,000美元更改為25萬美元,并添加另一個條件,以成為3歲以上的客戶),會發生什么。如果您使用規范,您只需更改單個類。如果您使用(復制/粘貼)相同的表達式,則需要更改它們。
可組合:您可以將多個規格來創建新的規范。這是另一種可重用性。
命名:PremiumCustomerSpecification更好地解釋了意圖,而不是復雜的表達。因此,如果您的業務有意義的表達式,請考慮使用規范。
可測試:一個規范是單獨(和容易)可測試的對象。
非業務表達式:您可以考慮不使用非業務相關表達式和操作的規范。
報告:如果你只是創建一個報表,不要創建規范,而是直接使用IQueryable。實際上,您甚至可以使用簡單的SQL,Views或其他工具進行報告。DDD不關心報告,并且從性能的角度來看,底層數據存儲的查詢優勢可能很重要。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。