产品规格

介绍

规范图案 是特定的设计模式,由此业务规则可以通过使用布尔逻辑链接业务规则一起被重组 (维基百科)。

实际上,它主要用于为实体或其他业务对象定义可重用的过滤器

在本节中,我们将看到对规范模式的需求本节是通用的,与ABP的实施无关。

假设您有一个计算客户总数的服务方法,如下所示:

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

您可能希望通过过滤器获得客户计数。例如,您可能拥有高级客户(余额超过100,000美元),或者您可能希望仅按注册年度过滤客户然后,您可以创建其他方法,如 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()方法,该方法返回一个表达式,用于更好地与IQueryableExpression树集成这样,我们可以轻松地将规范传递到存储库以在数据库级别应用过滤器。

我们通常继承自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);
    }
}

如您所见,我们只是实现了简单的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))
    {
    }
}

AndSpecificationSpecification的子类,仅当满足两个规范时才满足。然后,我们可以像使用任何其他规范一样使用NewPremiumCustomersSpecification:

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

讨论

虽然规范模式比C#lambda表达式旧,但它通常与表达式进行比较。一些开发人员可能认为不再需要它,我们可以直接将表达式传递到存储库或域服务,如下所示:

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

由于ABP的存储库支持尝试,这是一个完全有效的用途。您不必在应用程序中定义或使用任何规范,而是可以使用表达式。

那么,规范的重点是什么?我们为什么以及何时应该考虑使用它们?

何时使用?

使用规范的一些好处:

何时不使用?

nidie.com.cn - 用心与你沟通