1. 程式人生 > >ABP官方文檔翻譯 3.5 規約

ABP官方文檔翻譯 3.5 規約

繼承 indent .cn 為什麽 pre 文檔 orm func creating

規約

  • 介紹
    • 示例
  • 創建規範類
  • 使用倉儲規約
  • 組合規約
  • 討論
    • 什麽時候使用?
    • 什麽時候不使用?

介紹

  規約模式是一種特別的軟件設計模式,通過使用布爾邏輯將業務規則鏈接起來重新調配業務規則。(維基百科)。

  尤其是,它通常用來為實體或其他業務對象定義可復用的過濾器。

示例

  在這個部分,我們將看到規約模式的必要性。本部分是通用的,和ABP的實現沒有必然的關系。

  假定,有一個服務方法,計算所有客戶的總數量,如下所示:

public class CustomerManager
{
    public int GetCustomerCount()
    {
        
//TODO... return 0; } }

  你或許希望通過過濾器獲取客戶數量。例如,你有優質客戶(擁有超過100,000美元的客戶)或者想通過註冊年份過濾客戶。然後你創建了其他方法,如GetPremiumCustomerCount(),GetCustomerCountRegisteredYear(int year),GetPremiumCustomerCountRegisteredInYeaar(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和表達式樹集成。因此,我們可以輕松的傳遞規約到倉儲,並在數據庫級別應用過濾器。

  我們通常繼承Specification<T>類,而不是直接實現ISpecification<T>接口。Specification類自動實現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);
    }
}

  如你所見,我們僅僅實現了簡單的拉姆達表達式來定義規約。讓我們使用這些規約獲取客戶的數量:

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,Not和AndNot擴展方法進行組合使用。示例:

var count = customerManager.GetCustomerCount(new PremiumCustomerSpecification().And(new CustomerRegistrationYearSpecification(2017)));

  我們甚至可以基於已有的規約創建一個新的規約:

public class NewPremiumCustomersSpecification : AndSpecification<Customer>
{
    public NewPremiumCustomersSpecification() 
        : base(new PremiumCustomerSpecification(), new CustomerRegistrationYearSpecification(2017))
    {
    }
}

  AndSpecification是Specification類的一個子類,這個類只有兩個規約都滿足時才滿足。因此我們可以像其他規約那樣使用NewPremiumCustomersSpecification:

var count = customerManager.GetCustomerCount(new NewPremiumCustomersSpecification());

討論

  因為規約模式比C#拉姆達表達式久遠,它經常和表達式比較。一些開發者可能認為不再需要規約模式,我們可以直接傳遞表達式給倉儲或領域服務,如下:

var count = _customerRepository.Count(c => c.Balance > 100000 && c.CreationYear == 2017);

  因為ABP倉儲支持表達式,所以這種使用方式完全有效。你不需要在應用裏定義或使用任何規約,你可以繼續使用表達式。所以,規約的點是什麽?為什麽還有什麽時候我們該考慮使用它呢?

什麽時候使用?

  使用規約的一些好處:

  • 可復用:設想你在代碼的很多地方都需要使用PremiumCustomer過濾器。如果你使用表達式而不是創建規約,如果以後會更改“Premium Customer”的定義(比如,你想要更改優質的標準從$100000到$250000並且添加另一個條件如客戶必須大於3)將會放生什麽呢。如果你使用規約,僅僅需要更改一個類。如果你使用(復制/粘貼)同樣的表達式,需要全部更改他們。
  • 可組合:你可以組合多個規約創建一個新的規約。這是另一種類型的復用。
  • 命名的:PremiumCustomerSpecification比使用復雜的表達式更能清晰的表達意圖。所以,如果在業務中這個表達式是有意義的,考慮使用規約。
  • 可測試:規約是獨立易測試的對象。

什麽時候不使用?

  • 沒有業務表達式:你可以考慮不使用規約,如果表達式、操作沒有業務的話。
  • 報表:如果你僅僅創建一個報表,就不要創建規約,直接使用IQueryable。實際上,你甚至可以使用平常的SQL、師徒和其他報表工具。DDD對報表不怎麽關心,從性能角度來講,可以使用數據存儲查詢的好處是非常重要的。

返回主目錄

ABP官方文檔翻譯 3.5 規約