假设你正在减肥,不能吃肉,也不能吃卡路里大于500的食物。把这种情况用编程来表示就会是下面这样
void Eat(Food food)
{
if(food.HasMeat||food.Calorie>500)
{
Console.WriteLine("不吃");
return;
}
Console.WriteLine("吃");
}
很简单的卫语句搞定。
但是如果你是一个挑食者,你不吃鱼,虾,鸡蛋,牛奶等等,那上述的卫语句就会变得无比之长。
这时候我们就会想着把它抽取成一个单独的返回布尔值的方法。
当然现在只是结构上的抽取,还不能算是模式。
但是如果现在的用例是给所有类型的同学配餐,那这个方法就可以抽成可复用接口,形成模式。
在wiki中,对规约模式(Specification pattern)的定义是 a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic.
具体就是指将业务规则用布尔运算组合起来的一种设计模式。
规约模式的优势在于将业务逻辑从隐式(包含在某个方法中的卫语句),变成显式规则(提取成单独的类型)。
这种方法能够很明确的告诉开发者(尤其是后续的代码维护者),这个逻辑是一个特定的领域知识,提示开发者能够复用、检验和调整这些领域知识。(因此在DDD中规约模式很常见)
另外也有利于实现控制反转,将领域规则从实体对象或者服务内部,抽取到外部。
比如现在新来了一个不吃辣的同学,我们只需要新增一条不辣食物的规约,进行注入,而不需要深入食客对象进行更改。
规约模式和我们熟知的策略模式会很像。他们的共同点有:
但是也有以下以下不同:
所以我们可以简单的认为规约模式是一种特殊的策略模式。
规约模式也很容易遭到滥用,部分同学学习了规约模式后,就会想将所有的卫语句都提取成规约。
事实上这种方式会导致代码的复杂度增加,而有些领域知识如果是稳定的(不变的),那么放在实体类型中往往会更加合适。
参考文档:
本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/specification%E6%A8%A1%E5%BC%8F.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。