文档目录

本节内容:

简介

规约模式是一个特别的软件设计模式,业务逻辑可以使用boolean逻辑重新链接业务逻辑(维基百科).

实践中的大部分情况,它是为实体或其它业务对象,定义可复用的过滤器.

示例

在此小节,我们将看到规约模式的必要性,这节是通用的,与ABP的实现无关.

假设你有一个服务方法用来计算客户的总数,如:

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

你可能想通过一个过滤器获取客户数,例如:你有优质的客户(余额超过$100,000)或根据注册年份过滤客户,于是你可以创建其它方法,如:GetPremiumCustomerCount(),GetCustomerCountRegisteredInYear(int year),GetPremiumCustomerCountRegisterdInYear(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);
}

然后我们可以为一个customer调用 IsSatisfiedBy 来检查这个customer是否符合意图.因此,我们可用不同的过滤器调用相同的GetCustomerCount方法,不需要修改方法本身.

理论上这个方案很好了,但在C#中可以改进得更完美, 例如:从数据库中获取所有的客户来检查是否满足给定的规约/条件,这可不是很有效率,接下来的小节里,我们将看到ABP的实现,它解决了这个问题.

创建规约类

ABP定义了如下的ISpecification接口:

public interface ISpecification<T>
{
bool IsSatisfiedBy(T obj); Expression<Func<T, bool>> ToExpression();

添加一个  ToExpression() 方法,它返回一个表达式(expression),用来更好地结合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 >= );
}
} //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不是必要的,因此我们可以直接使用仓储和规约来查询数据库,但考虑一下:我们想在某客户上执行一个业务操作,我们可以使用规约和一个领域服务来指定customers,从而继续工作.

组合规约

有个强大的功能:可以用And,Or,Not 和 AndNot 扩展方法,组合规约.如:

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

我们甚至可以在已有的规约的基础上,创建一个新规约:

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

AndSpecification 是一个子类,它仅在两个规约都满足的情况下才符合条件,然后我们可以使用NewPremiumCustomersSpecification ,就像其它的规约一样:

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

讨论

由于规约模式比C#的lambda表达式旧,所以通过拿来与表达式对比.一些开发者可能认为规约模式不再需要,我们可以直接传递表达式给一个仓储或领域服务,如:

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

由于ABP的仓储支持表达式,这是一个合法的用法,在应用里,你不是一定要定义或使用任何规约,于是你可以继承使用表达式,所以,规约的要点在哪?为何,何时应当考虑使用它们?

何时使用

使用规约的好处:

  • 可复用:设想你需要在你代码里多处用到"优质客户"过滤,如果你使用表达式而不创建一个规约,那如果在以后你需要修改"优质客户"的定义会发生什么(如:你想改成余额至少从$100,000到250,000并包含其它条件,诸如客户注册超过3年),如果你使用规约,你只需要修改一个类,如果你使用(复制/粘贴)相同的表达式,你就要修改所有用到的地方.
  • 可组合:你可以联合多个规约来创建新规约,这是另一种形式的复用.
  • 命名化:PremiumCustomerSpecification比一个复杂的表达式更好地表述了意图, 所以,如果你想一个表达式在你的业务里变得可顾名思义,那么考虑使用规约.
  • 可测试:一个规约是一个单独的(且易于)可测试的对象.

何时不用

  • 没有业务表达式: 你可以在你的业务不涉及表达式和操作时不使用规约.
  • 创建报表:如果你只是创建一个报表就不要用规约,而直接使用IQueryable,实际上,你甚至可以使用原始的SQL,视图或其它报表工具.DDD不关心报表和底层数据库存储获取查询好处及视图性能.

英文原文:http://www.aspnetboilerplate.com/Pages/Documents/Specifications

ABP框架 - 规约的更多相关文章

  1. ABP框架 - N层架构

    目录 介绍 DDD分层 ABP架构模型 客户端 展现层 分布式服务层 应用层 领域层 基础设施层 介绍 在应用程序设计中,分层架构是一种被广泛使用的技术,它助于降低复杂度和提高代码的可重用性.在ABP ...

  2. 手动从0搭建ABP框架-ABP官方完整解决方案和手动搭建简化解决方案实践

      本文主要讲解了如何把ABP官方的在线生成解决方案运行起来,并说明了解决方案中项目间的依赖关系.然后手动实践了如何从0搭建了一个简化的解决方案.ABP官方的在线生成解决方案源码下载参考[3],手动搭 ...

  3. ABP入门系列(1)——学习Abp框架之实操演练

    作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...

  4. 一步一步使用ABP框架搭建正式项目系列教程

    研究ABP框架好多天了,第一次看到这个框架的名称到现在已经很久了,但由于当时内功有限,看不太懂,所以就只是大概记住了ABP这个名字.最近几天,看到了园友@阳光铭睿的系列ABP教程,又点燃了我内心要研究 ...

  5. C#高级知识点&(ABP框架理论学习高级篇)——白金版

    前言摘要 很早以前就有要写ABP高级系列教程的计划了,但是迟迟到现在这个高级理论系列才和大家见面.其实这篇博客很早就着手写了,只是楼主一直写写停停.看看下图,就知道这篇博客的生产日期了,谁知它的出厂日 ...

  6. 高薪诚聘熟悉ABP框架的.NET高级开发工程师(2016年7月28日重发)

    招聘单位是ABP架构设计交流群(134710707)群主阳铭所在的公司-上海运图贸易有限公司 招聘岗位:.NET高级开发工程师工作地点:上海-普陀区 [公司情况]上海运图贸易有限公司,是由易迅网的创始 ...

  7. ABP框架实践基础篇之开发UI层

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 说明 其实最开始写的,就是这个ABP框架实践基础篇.在写这篇博客之前,又回头复习了一下ABP框架的理论,如果你还没学习,请查看AB ...

  8. 一步一步使用ABP框架搭建正式项目系列教程之本地化详解

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 本篇目录 扯扯本地化 ABP中的本地化 小结 扯扯本地化 本节来说说本地化,也有叫国际化.全球化的,不管怎么个叫法,反正道理都是一 ...

  9. ABP框架搭建项目系列教程基础版完结篇

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 经过前面十二篇的基础教程,现在终于该做个总结了. 回顾 第一篇,我们建议新手朋友们先通过ABP官网的启动模板生成解决方案,因为这样 ...

随机推荐

  1. LSA和pLSA的比较

    Comparison   LSA pLSA 1. Theoretical background Linear Algebra Probabilities and Statistics 2. Objec ...

  2. [最短路][部分转]P1027 Car的旅行路线

    题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第I个城市中高速铁路了的单位 ...

  3. CCLuaObjcBridge - Lua 与 Objective-C 互操作的简单解决方案

    http://dualface.github.io/blog/2013/01/27/call-objectivec-from-lua/ 月初的时候,发了一篇关于 Lua 与 Java 互操作的文章,里 ...

  4. NETStandard,NETFx,Mono,NETCore,ASPNetCore 之间关系的整理

    因为现在很多人对这几者之间的关系还不甚了解,这里根据我所知来做一个大概的介绍...... .NET Standard:         .NET标准,只要符合这个标准实现类库,即可在支持此标准的Run ...

  5. 十六、Hadoop学习笔记————Zookeeper实战

    所有服务器都会先将自己的服务器信息注册到servers中,然后每台服务器都会尝试注册master,哪台注册成功,则哪台就是master服务器. 所有的服务器都会关注master节点的删除事件,这样通过 ...

  6. C语言中静态申请内存遇到的错误分析

    今天调试代码中,遇到了一个比较奇怪的打印,dump出来的数据只有前四位有值,其他后面的都为零. 出于直觉,应该是内存没有申请到.仔细核对代码之后,果真发现了一个语法错误,就是使用指针的指针时 ,对申请 ...

  7. Hive详解

    1.   Hive基本概念 1.1  Hive简介 1.1.1 什么是Hive Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能. 1.1 ...

  8. continue,break以及加上标签的使用(goto思路)

    代码例子在java编程思想70-73页.这里只是想做做总结 java中需要用到标签的唯一理由就是因为由循环嵌套的存在,而且想从多层嵌套循环中break或者continue. 因此,标签只能放在循环前面 ...

  9. webpack中实现按需加载

    webpack中的require.ensure()可以实现按需加载资源包括js,css等,它会给里面require的文件单独打包,不和主文件打包在一起,webpack会自动配置名字,如0.js,1.j ...

  10. [PHP基础]有关isset empty 函数的面试题

    用isset()和empty()判断下面的变量. $str = ''; $int = 0 ; $arr = array(); isset($str) 返回的是 true 还是 false empty( ...