step_by_step_ABP规约模式
一段时间没有在github 上浏览ABP项目,几天前看到ABP新增规约模式,开始了解并学习文档 记录一下
Introduction
介绍
Specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic (Wikipedia).
规约模式是一种特定的软件设计模式,业务逻辑可以通过布尔逻辑进行重组 -维基百科
In pratical, it's mostly used to define reusable filters for entities or other business objects.
在实际中 ,它最主要被用于为实体或者是其他的业务对象定义可重用过滤器
Example
例子
In this section, we will see the need for specification pattern. This section is generic and not related to ABP's implementation.
在本节中,我们将看到需要规约模式,本节是通用的并且与ABP的实现无关
Assume that you have a service method that calculates total count of your customers as shown below:
假设你有一个用于计算客户总数的服务方法 如下所示
public class CustomerManager
{
public int GetCustomerCount()
{
// TODO ...
return ;
}}
}}
You probably will want to get customer count by a filter. For example, you may have premium customers (which have balance more than $100,000) or you may want to filter customers just by registration year. Then you can create other methods like GetPremiumCustomerCount(), GetCustomerCountRegisteredInYear(int year),GetPremiumCustomerCountRegisteredInYear(int year) and more.
你可能希望通过过滤器获得客户数,例如 你也许有高级客户(余额超过100000美元) 或者你希望通过注册年限过滤客户.然后你会创建很多像 GetPremiumCustomerCount(),GetCustomerCountRegisteredInYear(int year) ,GetPremiumCustomerCountRegisteredInYear(int year)这样的方法,
As you have more criteria, it's not possible to create a combination for every possibility
由于你有更多的条件,因此无法为每一种可能性创建组合.
One solution to this problem is the specification pattern. We could create a single method that gets a parameter as the filter:
这个问题的解决方案便是规约模式,我们可能创建一个单独的方法为过滤器获取参数:
public class CustomerManager
{
3 private readonly IRepository<Customer> _customerRepository; public CustomerManager(IRepository<Customer> customerRepository)
6 {
_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;
}
}
Thus, we can get any object as parameter that implements ISpecification<Customer> interface which is defined as shown below:
因此,我们可以得到一个实现了ISpecification<Customer> 接口的参数 ,其定义如下所示:
public interface ISpecification<T>
{
bool IsSatisfiedBy(T obj);
}
And we can call IsSatisfiedBy with a customer to test if this customer is intented. Thus, we can use same GetCustomerCount with different filters without changing the method itself.
并且我们可以调用 IsSatisfiedBy 来测试是否为被期望的客户,像这样,我们可以使用相同 方法GetCustomerCount与不同的过滤器,而不用改变方法本身.
While this solution is pretty fine in theory, it should be improved to better work in C#. For instance, it's not efficient to get all customers from database to check if they satisfy the given specification/condition. In the next section, we will see ABP's implementation which overcome this problem.
虽然这个解决方案在理论上非常好,但是在C#工作中应该被提升的更好,对于这个例子,在数据库中获得所有的客户并检查是否符合给定的规定/条件是很影响性能的.在下一节中,我们将看到ABP克服这个问题的实现。
Creating Specification Classes
创建规范类
ABP defines the ISpecification interface as shown below:
ABP 定义接口 ISpecification 如下所示:
public interface ISpecification<T>
{
bool IsSatisfiedBy(T obj); Expression<Func<T, bool>> ToExpression();
}
Adds a ToExpression() method which returns an expression and used to better integrate with IQueryable and Expression trees. Thus, we can easily pass a specification to a repository to apply a filter in the database level.
添加了 ToExpression() 返回表达式的方法并用于更好的与 IQueryable 和表达式集成。像这样,我们可以容易将规则传递给仓储 以在数据库级别应用过滤器
We generally inherit from Specification<T> class instead of directly implementing ISpecification<T> interface. Specification class automatically implements IsSatisfiedBy method. So, we only need to define ToExpression. Let's create some specification classes:
我们通常继承 Specification<T> 类而不是直接实现 ISpecification<T> 接口 .Specification 类 自动的实现 IsSatisfiedBy方法 .因此 我们仅仅需要去定义ToExpression . 让我们创建 一些 specification 类 :
//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)
{
17 Year = year;
} 20 public override Expression<Func<Customer, bool>> ToExpression()
21 {
return (customer) => (customer.CreationYear == Year);
}
}
As you see, we just implemented simple lambda expressions to define specifications. Let's use these specifications to get count of customers:
正如你看到的,我们仅仅实现一个lambda 表达式去定义 规则.让我们使用这些规则去得到客户数:
var count = customerManager.GetCustomerCount(new PremiumCustomerSpecification());
var count = customerManager.GetCustomerCount(new CustomerRegistrationYearSpecification());
Using Specification With Repository
在仓储中使用规则
Now, we can optimize CustomerManager to apply filter in the database:
现在 ,我们可以优化 CustomerManager 以在数据库中应用过滤器:
{
private readonly IRepository<Customer> _customerRepository;
public CustomerManager(IRepository<Customer> customerRepository)
{
_customerRepository = customerRepository;
}
public int GetCustomerCount(ISpecification<Customer> spec)
{
return _customerRepository.Count(spec.ToExpression());
}
}
It's that simple. We can pass any specification to repositories since repositories can work with expressions as filters. In this example, CustomerManager is unnecessary since we could directly use repository with the specification to query database. But think that we want to execute a business operation on some customers. In that case, we could use specifications with a domain service to specify customers to work on.
就是这么简单 .我们可以将任何规则传递给仓储,因为仓储可以将表达式作为过滤器.在这个例子中 ,CustomerManager 是不必要的,因为我们可以直接使用仓储通过 规则去查询数据库 ,但是我们想要在一些客户上执行一个业务逻辑操作.在这种情况下,我们可以使用具有领域服务的规范来指定要处理的客户。
Composing Specifications
组合规则
One powerful feature of specifications is that they are composable with And, Or, Not and AndNot extension methods. Example:
规约一个强大的功能是,它们可以组合使用AND,OR和 ANDNOT 扩展方法 例:
var count = customerManager.GetCustomerCount(new PremiumCustomerSpecification().And(new CustomerRegistrationYearSpecification()));
We can even create a new specification class from existing specifications:
我们甚至可以 从现有的规则类创建一个 新的规则类:
public class NewPremiumCustomersSpecification : AndSpecification<Customer>
{
public NewPremiumCustomersSpecification()
: base(new PremiumCustomerSpecification(), new CustomerRegistrationYearSpecification(2017))
{
}
}
AndSpecification is a subclass of Specification class which satisfies only if both specifications are satisfied. Then we can use NewPremiumCustomersSpecification just like any other specification:
AndSpecification是Specification 类的子类,只有满足两个规则时才满足.然后我们可以像其他规则一样使用NewPremiumCustomersSpecification.
var count = customerManager.GetCustomerCount(new NewPremiumCustomersSpecification());
Discussion
讨论
While specification pattern is older than C# lambda expressions, it's generally compared to expressions. Some developers may think it's not needed anymore and we can directly pass expressions to a repository or to a domain service as shown below:
虽然规约模式早于 C# lambda 表达式 ,但是通常与表达式相比较.一些开发人员也许认为不在需要它,我们可以直接将表达式传递给仓储或者领域服务 如下所示:
var count = _customerRepository.Count(c => c.Balance> && c.CreationYear == );
Since ABP's Repository supports expessions, this is completely a valid usage. You don't have to define or use any specification in your application and you can go with expressions. So, what's the point of specification? Why and when should we consider to use them?
由于ABP的仓储支持 表达式,这是一个完全有效的用法,在应用程序中你不必去定义或使用任何规则,你可以使用表达式,那么规约的要点是什么?我们在什么时候 什么情况下会用到它们呢?
When To Use?
何时使用 ?
Some benefits of using specifications:
使用规约的一些好处:
Reusabe: Think that you need to PremiumCustomer filter in many places in your code base. If you go with expressions and not create a specification, what happens if you later change "Premium Customer" definition (say, you want to change minimum balance from $100,000 to $250,000 and add another condition like to be a customer older than 3). If you used specification, you just change a single class. If you used (copy/paste) same expression, you need to change all of them.
可重用的:在你的代码库的许多地方都要使用到PremiumCustomer 过滤器,如果你使用的是表达式而不是创建一个规约,如果你以后更改了'高级客户'的定义(例如,你想从最低余额$100000改变到$250000并且添加了如客户的年龄超过3岁这样的条件).如果你使用了规约,你仅仅改变单一类.如果你使用(粘贴/复制)一些表达式,你需要更改所有的表达式 .
Composable: You can combine multiple specification to create new specifications. This is another type of reusability.
组合:你可以把多个规则组合起来创建一个新规则,这是另一种类型的可重用性.
Named: PremiumCustomerSpecification better explains the intent rather than a complex expression. So, if you have an expression that is meaningful in your business, consider to use specifications.
命名的:PremiumCustomerSpecification 更好的解释了意图,而不是复杂的表达式.那么如果在你的业务中有一个有意义的表达式,请考虑使用规约.
Testable: A specification is separately (and easily) testable object.
可测试: 规约是单独的(并且容易)可测试对象.
When To Not Use?
何时不要去使用?
Non business expressions: You can consider to not use specifications for non business related expressions and operations.
没有业务的表达式:对于那些没有涉及业务的表达式和操作你可以决定不使用规约.
Reporting: If you are just creating a report do not create specifications, but directly use IQueryable. Actually, you can even use plain SQL, Views or another tool for reporting. DDD does not care on reporting much and getting querying benefits of underlying data store can be important from performance point of view.
报告:如果你只是创建一个报告,不是创建规范,而是直接使用IQueryable。实际上,您甚至可以使用纯SQL,视图或其他工具进行报告。DDD不关心报告太多,从性能的角度来看,获取底层数据存储的查询优势可能很重要。
step_by_step_ABP规约模式的更多相关文章
- 生产环境下实践DDD中的规约模式
最近的开发工作涉及到两个模块“任务”和“日周报”.关系是日周报消费任务,因为用户在写日周报的时候,需要按一定的规则筛选当前用户的任务,作为日周报的一部分提交.整个项目采用类似于Orchard那种平台加 ...
- [.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了 ...
- [.NET领域驱动设计实战系列]专题三:前期准备之规约模式(Specification Pattern)
一.前言 在专题二中已经应用DDD和SOA的思想简单构建了一个网上书店的网站,接下来的专题中将会对该网站补充更多的DDD的内容.本专题作为一个准备专题,因为在后面一个专题中将会网上书店中的仓储实现引入 ...
- DDD~领域服务的规约模式
回到目录 规 约(Specification)模式:第一次看到这东西是在microsoft NLayer项目中,它是微软对DDD的解说,就像petshop告诉了我们MVC如何使用一样,这个规约模式最重 ...
- 规约模式(Specification Pattern)
前期准备之规约模式(Specification Pattern) 一.前言 在专题二中已经应用DDD和SOA的思想简单构建了一个网上书店的网站,接下来的专题中将会对该网站补充更多的DDD的内容.本专题 ...
- 规约模式(Specification Pattern)
一.引言 最近在看一个项目的源码时(DDD),对里面的一些设计思想和设计思路有了一些疑问.当看到(Repository层)中使用了 spec.SatisfiedBy() 时,感觉有点懵.于是在项目中搜 ...
- 规约模式Specification Pattern
什么是规约模式 规约模式允许我们将一小块领域知识封装到一个单元中,即规约,然后可以在code base中对其进行复用. 它可以用来解决在查询中泛滥着GetBySomething方法的问题,以及对查询条 ...
- 设计模式:规约模式(Specification-Pattern)
"其实地上本没有路,走的人多了,也便成了路"--鲁迅<故乡> 这句话很好的描述了设计模式的由来.前辈们通过实践和总结,将优秀的编程思想沉淀成设计模式,为开发者提供了解决 ...
- 规约模式Specification的学习
最近一直在看DDD开发 规约似乎用得很普遍. 但是还是理解不了.所以记录下学习的进度.- 规约(Specification)模式 目的:查询语句和查询条件的分离 写了一个关于规约的模拟小程序 cla ...
随机推荐
- python3学习笔记十(循环语句)
参考http://www.runoob.com/python3/python3-loop.html 循环语句 while循环 # !/usr/bin/env python3 n = 100 sum = ...
- [UE4]让Spline具象化
接上一个实例 一.在TestSpline蓝图,切换到蓝图构造函数Constrction Script事件中,添加如下代码: 二.别忘记个Add Spline Mesh Component设置Stati ...
- NPOI 导出Excel部分
) { MessageBox.Show("没有找到相关的数据!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.I ...
- jupyter依赖tornado版本
使用jupyter莫名奇妙出现500错误,发现是更新tornado出了问题,我的jupyter版本是5.7.4不支持6.x版本的tornado,回退到5.x版本的tornado就好了. pip ins ...
- win10系统搜索不到某些老式打印机
问题: win10系统在设置选项里面搜索打印机,却找不到打印机. 百度了一下: 基本说的是驱动问题. 解决方法: 1.下载lansee局域网扫描软件 2.打开嗅探工具点击开始 3.搜索出来之后,会在共 ...
- 小程序navigateBack,子页面传值给父页面
子页面 let page = getCurrentPages(); let prevPage = page[page.length - 2]; prevPage.setData({ lxr :item ...
- gunicorn开启、关闭和重启
1.gunicorn开启 在项目的根目录下,输入下方的代码. gunicorn --bind unix:/tmp/域名.socket projectname.wsgi:application 2.查询 ...
- react基础学习 二——生命周期
生命周期mount: mounting装载创建 update更新 unmounting卸载 错误捕获 注意点:生命周期函数的 作用,什么之后用 只有类式组件有生命周期,函数式组件没有生命周期 moun ...
- 小程序构建npm出现没有找到node_modules
以下转自:https://blog.csdn.net/zhangyabo_code/article/details/86162671 npm initnpm install --production ...
- sql 查询语句的练习
--1.使用基本查询语句. --(1)查询DEPT表显示所有部门名称. select * from dept; --(2)查询EMP表显示所有雇员名及其全年收入(月收入=工资+补助),处理NULL行, ...