介绍

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

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

在本节中,我们将看到需要规格模式。本节是通用的,与ABP的实现无关。

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

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

您可能希望通过过滤器获得客户数量。例如,您可能会有高级客户(其余额超过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 = ; 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 >= );
}
} //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());

使用规范与存储库

现在,我们可以优化 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()));
 

我们甚至可以从现有规范中创建一个新的规范类:

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

规范Specification 类的一个子类,只有在两个规范都满足的时候才能满足。那么我们可以像其他规格一样使用NewPremiumCustomersSpecification:

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

讨论

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

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

由于ABP的存储库支持expessions,这是完全有效的用法。您不必在应用程序中定义或使用任何规范,您可以使用表达式。那么说明什么呢?为什么和何时应该考虑使用它们?

何时使用?

使用规格的一些好处:

  • Reusabe:认为您需要在您的代码库中的许多地方使用PremiumCustomer过滤器。如果您使用表达式而不是创建规范,如果您以后更改“高级客户”定义(例如,要将最终余额从100,000美元更改为25万美元,并添加另一个条件,以成为3岁以上的客户),会发生什么。如果您使用规范,您只需更改单个类。如果您使用(复制/粘贴)相同的表达式,则需要更改它们。
  • 可组合:您可以将多个规格来创建新的规范。这是另一种可重用性。
  • 命名:PremiumCustomerSpecification更好地解释了意图,而不是复杂的表达。因此,如果您的业务有意义的表达式,请考虑使用规范。
  • 可测试:一个规范是单独(和容易)可测试的对象。

何时不使用?

  • 非业务表达式:您可以考虑不使用非业务相关表达式和操作的规范。
  • 报告:如果你只是创建一个报表,不要创建规范,而是直接使用IQueryable。实际上,您甚至可以使用简单的SQL,Views或其他工具进行报告。DDD不关心报告,并且从性能的角度来看,底层数据存储的查询优势可能很重要。

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

  1. 终端I/O之非规范模式

    关闭termios结构中c_lflag字段的ICANON标志就使终端处于非规范模式.在非规范模式中,输入数据并不组成行,不处理下列特殊字符:ERASE/KILL/EOF/NL/EOL/EOL2/CR/ ...

  2. 终端I/O之规范模式

    规范模式很简单:发一个读请求,输入完一行后,终端驱动程序即刻返回.下列几个条件都会造成读返回: 所要求的字节数已经读到时,读返回.无需读一个完整的行.如果都了部分行,也不会丢失任何信息,下一次读从前一 ...

  3. ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十三节--RBAC模式及ABP权限管理(附送福利)

    ABP+AdminLTE+Bootstrap Table权限管理系统一期 Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate- ...

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

    网页错误详细信息消息: 对象不支持此属性或方法 document.getElementsByClassName('element_name') 需要自己实现下该方法,因为ie5之前的版本并不支持这个方 ...

  5. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  6. 前端面霸系列(1):doctype 、Quirks Mode & Standards Mode 、document.compatMode

    近几日,气压猛降,雾霾铺天盖地,眼看一场腥风血雨就要在前端江湖爆发,这场战争不仅是百度.腾讯.阿狸.搜狐网易新浪等江湖豪门抢夺人才的大战,也是诸位江湖人士重新洗牌的好时机.每年10月,江湖的波动胜过华 ...

  7. JS魔法堂:浏览器模式和文档模式怎么玩?

    一.前言 从IE8开始引入了文档兼容模式的概念,作为开发人员的我们可以在开发人员工具中通过“浏览器模式”和“文档模式”(IE11开始改为“浏览器模式”改成更贴切的“用户代理字符串”)品味一番,它的出现 ...

  8. ABP框架系列之四十八:(Specifications-规范)

    Introduction Specification pattern is a particular software design pattern, whereby business rules c ...

  9. 浏览器根对象document之字符串属性

    1.1 停止使用的属性 fgColor.linkColor.vlinkColor.alinkColor.bgColor. 1.2 文档地址 document.URL 与documentURI属性返回同 ...

随机推荐

  1. 4063: [Cerc2012]Darts

    4063: [Cerc2012]Darts Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 85  Solved: 53[Submit][Status] ...

  2. 1011: [HNOI2008]遥远的行星

    1011: [HNOI2008]遥远的行星 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 2241  Solved ...

  3. Java数据结构之Set学习总结

    前言: 前面介绍了Java的数据结构List.Map,今天抽空学习总结一下另一种数据结构Set. Set介绍 Set相对于List.Map是最简单的一种集合.集合中的对象不按特定的方式排序,并且没有重 ...

  4. 循环单词 java

    链接:https://www.nowcoder.com/questionTerminal/9d5fbe7750a34d0b91c73943f93b2d7d来源:牛客网如果一个单词通过循环右移获得的单词 ...

  5. Druid连接池配置(java无框架)

    连接池是一个对数据库连接进行管理的东西,当一个线程需要用 JDBC 对 数据库操作时,它从池中请求一个连接.当这个线程使用完了这个连接,将它返回到连接池中,这样这就可以被其它想使用它的线程使用,而不是 ...

  6. 在Pypi上发布自己的Python包

    使用Python编程的都知道,Python的包安装非常的方便,一般都是可以pip来安装搞定: sudo pip install <package name> pip的安装请移步:https ...

  7. Python自动生产表情包

    作为一个数据分析师,应该信奉一句话--"一图胜千言".不过这里要说的并不是数据可视化,而是一款全民向的产品形态--表情包!!!! 表情包不仅仅是一种符号,更是一种文化--是促进社交 ...

  8. Mysql 保留最新的10条数据

    Mysql每天执行计划,保留最新的10条数据,其余的删除 1.Mysql 保留最新的10条数据 sql语句: DELETE tb FROM tbname AS tb,( SELECT id FROM ...

  9. laravel的延迟消息队列

    laravel的延迟消息队列 这篇来自于看到朋友转的58沈剑的一篇文章:1分钟实现"延迟消息"功能(http://mp.weixin.qq.com/s?__biz=MjM5ODYx ...

  10. Maven——快速入门手册(学习记录)

    前言: 前段时间进行了一点maven的入门学习,在这里做个记录,希望能帮到一些正在学习的朋友们.maven版本为3.3.9.希望大家觉得好的点个赞,觉得不好的多提提意见和建议做个交流.这里也贴出我学习 ...