规范模式-------From ABP Document

介绍

规范模式是一种特定的软件设计模式,通过使用布尔逻辑 (维基百科)将业务规则链接在一起,可以重新组合业务规则

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

在本节中,我们将看到需要规格模式。本节是通用的,与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不关心报告,并且从性能的角度来看,底层数据存储的查询优势可能很重要。
时间: 2024-11-02 09:20:29

规范模式-------From ABP Document的相关文章

规范模式【其他模式】

规范模式 @Slf4j public class Specification { /** * 规范模式: * Specification pattern separates the statement of how to match a candidate, * from the candidate object that it is matched against. * As well as its usefulness in selection, * it is also valuable

解决 IE 或者兼容模式不支持 document.getElementsByClassName() 的方法

网页错误详细信息消息: 对象不支持此属性或方法 document.getElementsByClassName('element_name') 需要自己实现下该方法,因为ie5之前的版本并不支持这个方法 有以下四种方法,第四种兼容性最好 一. //create method getElementsByClassName for document if(!document.getElementsByClassName){ document.getElementsByClassName = func

关于 document.compatMode

今天查资料时无意发现一个以前没有注意到过的属性:document.compatMode 经过一番资料的查询后,了解到以下信息: 我们都知道IE有两种盒子模型,在不声明 !DOCTYPE 时是混杂模式 (Quirks Mode),在声明了 !DOCTYPE 时与其他标准浏览器一致,是标准模式 (Standards Mode) document.compatMode 有两个属性值: BackCompat ----- 表示标准规范模式关闭,即当前为混杂模式 (Quirks Mode),此时浏览器客户区

深入理解浏览器兼容性模式

摘要:关于各种浏览器模式,网上已经有许多文档和资料了,但是很少有能够完全将几个概念阐述清楚的.大部分的资料稍显过时,有些内容可能已经不再适用了.本文中笔者将尽可能将几个概念阐述清楚,并去掉一些过时的内容,仅保留必要的干货. 想必你一定知道浏览器有个标准(Standards)模式和一个怪异(Quirks)模式,或许你还听说过有个"准标准(Almost Standards)"模式.而当你打开Internet Explorer的时候,又看到了什么浏览器模式.文档模式,还有什么兼容性视图等等.

浏览器兼容性问题之渲染模式——1

兼容性问题 目前市面上流行的浏览器有多种,这些浏览器在处理一个相同的页面时,表现或行为有时会有差异.这种差异可能很小,甚至不会被注意到:也可能很大,甚至造成在某个浏览器下无法正常浏览.我们把引起这些差异的问题统称为“浏览器兼容性问题”. 浏览器的内核 各品牌浏览器的内核是不同的,不同的内核处理同一段代码的时候思路不同.因此可以说,浏览器间内核的差异是产生兼容性问题的根本原因. “内核”也称为“引擎”.常见的浏览器及其渲染引擎(又称排版引擎).脚本引擎的列表如下: Browser Name Lay

浅谈HTML文档模式

不知道爱多想的你有没有在编写HTML代码时思考过 <!DOCTYPE html> 或是这一长串看都看不懂的 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 的代码,它是做什么的,为什么要有这句话,这句话起什么作用,它和其他HTML代码有关吗?嘤嘤嘤我才不回告诉你在前端的面试题中会经常看到对这个知识点的提问,而我

浏览器模式和文档模式怎么玩?

一.前言 从IE8开始引入了文档兼容模式的概念,作为开发人员的我们可以在开发人员工具中通过“浏览器模式”和“文档模式”(IE11开始改为“浏览器模式”改成更贴切的“用户代理字符串”)品味一番,它的出现极大地方便了苦逼的前端攻城狮们适配各版本的IE,但jser们也不能完全信任它,因为它只是提供尽可能的文档模式模拟而已. 本篇大部分内容来源于官方解说:http://msdn.microsoft.com/library/cc288325(v=vs.85).aspx,并尽量融入个人平常工作中踩过的坑加以

Linux串口IO模式的一些心得

众所周知,在Linux系统下所有设备都是以文件的形式存在,串口也一样. 通常I/O操作都是有阻塞与非阻塞的两种方式. 其中"超时"这个概念其实是阻塞中的一种处理手段,本质还是属于阻塞的I/O模式. 在Linux中串口的IO操作 本文将它分为三种状态: 阻塞状态 超时状态 非阻塞状态 这三种状态的转换组合有这么几种: 阻塞 --> 超时 阻塞 --> 非阻塞 超时 --> 阻塞 超时 --> 非阻塞 非阻塞 --> 阻塞 我们一个一个来分析 首先在一个串口的

delphi 11 编辑模式 浏览模式

编辑模式 浏览模式 设置焦点 //在使用前需要Webbrowser已经浏览过一个网页 否则错误 uses MSHTML; ///获取Webbrowser编辑模式里面的内容 procedure EditMode();begin  (Form1.WebBrowser1.Document as IHTMLDocument2).designMode := 'on';end;//----------------------------------------------------------------