今天和大家介绍一下一种特殊的设计模式——规约模式(Specification pattern)


什么是规约模式(Specification pattern)

假设你正在减肥,不能吃肉,也不能吃卡路里大于500的食物。把这种情况用编程来表示就会是下面这样

void Eat(Food food)
{
    if(food.HasMeat||food.Calorie>500)
    {
        Console.WriteLine("不吃");
        return;
    }
   
    Console.WriteLine("吃");
    
}

很简单的卫语句搞定。

但是如果你是一个挑食者,你不吃鱼,虾,鸡蛋,牛奶等等,那上述的卫语句就会变得无比之长。

这时候我们就会想着把它抽取成一个单独的返回布尔值的方法。

当然现在只是结构上的抽取,还不能算是模式。

但是如果现在的用例是给所有类型的同学配餐,那这个方法就可以抽成可复用接口,形成模式。

image-20200507202929295

定义

在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 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系