本文节选自《设计模式就该这样学》

1 规格模式的定义

规格模式(Specification Pattern)可以认为是组合模式的一种扩展。很多时候程序中的某些条件决定了业务逻辑,这些条件就可以抽离出来以某种关系(与、或、非)进行组合,从而灵活地对业务逻辑进行定制。另外,在查询、过滤等应用场合中,通过预定义多个条件,然后使用这些条件的组合来处理查询或过滤,而不是使用逻辑判断语句来处理,可以简化整个实现逻辑。

这里的每个条件都是一个规格,多个规格(条件)通过串联的方式以某种逻辑关系形成一个组合式的规格。规格模式属于结构型设计模式。

2 规格模式的应用场景

规格模式主要适用于以下应用场景。

(1)验证对象,检验对象本身是否满足某些业务要求或者是否已经为实现某个业务目标做好了准备。

(2)从集合中选择符合特定业务规则的对象或对象子集。

(3)指定在创建新对象的时候必须要满足某种业务要求。

3 规格模式的UML类图

规格模式的UML类图如下图所示。

由上图可以看到,规格模式主要包含6个角色。

(1)抽象规格书(Specification):对规格书的抽象定义。

(2)组合规格书(CompositeSpecification):一般设计为抽象类,对规格书进行与或非操作,实现and()、or()、not()方法,在方法中关联子类,因为子类为固定类,所以父类可以进行关联。

(3)与规格书(AndSpecification):对规格书进行与操作,实现isSatisfiedBy()方法。

(4)或规格书(OrSpecification):对规格书进行或操作,实现isSatisfiedBy()方法。

(5)非规格书(NotSpecification):对规格书进行非操作,实现isSatisfiedBy()方法。

(6)业务规格书(BizSpecification):实现isSatisfiedBy()方法,对业务进行判断,一个类为一种判断方式,可进行扩展。

4 规格模式的通用写法

以下是规格模式的通用写法。


public class Client { public static void main(String[] args) {
//待分析的对象
List<Object> list = new ArrayList<Object>();
//定义两个业务规格书
ISpecification spec1 = new BizSpecification("a");
ISpecification spec2 = new BizSpecification("b");
//规格调用
for (Object o : list) {
if(spec1.and(spec2).isSatisfiedBy(o)){ //如果o满足spec1 && spec2
System.out.println(o);
}
}
} //抽象规格书
interface ISpecification {
//候选者是否满足条件
boolean isSatisfiedBy (Object candidate) ;
//and操作
ISpecification and (ISpecification spec);
//or操作
ISpecification or (ISpecification spec);
//not操作
ISpecification not ();
} //组合规格书
static abstract class CompositeSpecification implements ISpecification {
//是否满足条件由子类实现
public abstract boolean isSatisfiedBy (Object candidate) ;
//and操作
public ISpecification and (ISpecification spec) {
return new AndSpecification(this, spec);
}
//or操作
public ISpecification or(ISpecification spec) {
return new OrSpecification(this, spec);
}
//not操作
public ISpecification not() {
return new NotSpecification(this);
}
} //与规格书
static class AndSpecification extends CompositeSpecification {
//传递两个规格书进行and操作
private ISpecification left;
private ISpecification right; public AndSpecification(ISpecification left, ISpecification right) {
this.left = left;
this.right = right;
} //进行and运算
public boolean isSatisfiedBy(Object candidate) {
return left.isSatisfiedBy(candidate) && right.isSatisfiedBy(candidate);
}
} static class OrSpecification extends CompositeSpecification {
//传递两个规格书进行or操作
private ISpecification left;
private ISpecification right; public OrSpecification(ISpecification left, ISpecification right) {
this.left= left;
this.right = right;
} //进行or运算
public boolean isSatisfiedBy(Object candidate) {
return left.isSatisfiedBy(candidate) || right.isSatisfiedBy(candidate);
}
} static class NotSpecification extends CompositeSpecification {
//传递两个规格书进行非操作
private ISpecification spec; public NotSpecification(ISpecification spec) {
this.spec = spec;
} //进行not运算
public boolean isSatisfiedBy(Object candidate) {
return !spec.isSatisfiedBy(candidate);
}
} //业务规格书
static class BizSpecification extends CompositeSpecification {
//基准对象,如姓名等,也可以是int等类型
private String obj;
public BizSpecification(String obj) {
this.obj = obj;
}
//判断是否满足要求
public boolean isSatisfiedBy(Object candidate){
//根据基准对象判断是否符合
return true;
}
}
}

5 规格模式的优缺点

5.1 优点

规格模式非常巧妙地实现了对象筛选功能,适合在多个对象中筛选查找,或者业务规则不适于放在任何已有实体或值对象中,而且规则变化和组合会掩盖对象的基本含义的情况。

5.2 缺点

规格模式中有一个很严重的问题就是父类依赖子类,这种情景只有在非常明确不会发生变化的场景中存在,它不具备扩展性,是一种固化而不可变化的结构。一般在面向对象设计中应该尽量避免。

关注微信公众号『 Tom弹架构 』回复“设计模式”可获取完整源码。

【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom弹架构 』可获取更多技术干货!

规格模式(Specification Pattern)的更多相关文章

  1. 规约模式(Specification Pattern)

    前期准备之规约模式(Specification Pattern) 一.前言 在专题二中已经应用DDD和SOA的思想简单构建了一个网上书店的网站,接下来的专题中将会对该网站补充更多的DDD的内容.本专题 ...

  2. [.NET领域驱动设计实战系列]专题三:前期准备之规约模式(Specification Pattern)

    一.前言 在专题二中已经应用DDD和SOA的思想简单构建了一个网上书店的网站,接下来的专题中将会对该网站补充更多的DDD的内容.本专题作为一个准备专题,因为在后面一个专题中将会网上书店中的仓储实现引入 ...

  3. 规约模式Specification Pattern

    什么是规约模式 规约模式允许我们将一小块领域知识封装到一个单元中,即规约,然后可以在code base中对其进行复用. 它可以用来解决在查询中泛滥着GetBySomething方法的问题,以及对查询条 ...

  4. 规约模式(Specification Pattern)

    一.引言 最近在看一个项目的源码时(DDD),对里面的一些设计思想和设计思路有了一些疑问.当看到(Repository层)中使用了 spec.SatisfiedBy() 时,感觉有点懵.于是在项目中搜 ...

  5. 设计模式(十二):通过ATM取款机来认识“状态模式”(State Pattern)

    说到状态模式,如果你看过之前发布的重构系列的文章中的<代码重构(六):代码重构完整案例>这篇博客的话,那么你应该对“状态模式”并不陌生,因为我们之前使用到了状态模式进行重构.上一篇博客我们 ...

  6. 设计模式(十):从电影院中认识"迭代器模式"(Iterator Pattern)

    上篇博客我们从醋溜土豆丝与清炒苦瓜中认识了“模板方法模式”,那么在今天这篇博客中我们要从电影院中来认识"迭代器模式"(Iterator Pattern).“迭代器模式”顾名思义就是 ...

  7. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  8. Net设计模式实例之原型模式( Prototype Pattern)

    一.原型模式简介(Brief Introduction) 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. Specify the kin ...

  9. 代理模式(Proxy pattern)

    代理模式(proxy pattern):作用:为其他对象提供一种代理,以控制对这个对象的访问.代理对象在客户端对象和目标对象之间起中介的作用. 代理模式涉及到的角色: 抽象角色:声明真实对象和代理对象 ...

随机推荐

  1. 『基于ArcGIS的Python编程秘籍(第2版)』书本源码

    ArcPy学习 第1章 面向ArcGIS的Python编程语言的基础 略 第2章 管理地图文档和图层 引用当前的地图文档 引用磁盘上的地图文档 获取地图文档的图层列表 限制图层列表 缩放至所选要素 改 ...

  2. Kubernetes-Service介绍(三)-Ingress(含最新版安装踩坑实践)

    前言 本篇是Kubernetes第十篇,大家一定要把环境搭建起来,看是解决不了问题的,必须实战. Kubernetes系列文章: Kubernetes介绍 Kubernetes环境搭建 Kuberne ...

  3. PostMan生成的测试报告 工具node.js、步骤、结果

    Postman生成测试报告的工具node.js 1.下载并安装: win系统(下载后一直下一步就好了) mac系统  2.配置环境 (1).在命令提示符里面输入npm 检验安装是否成功可以输入命令:n ...

  4. '\r'(回车符),'\n'(换行符)与"\r\n"

    一.'\n','\r'和"\r\n" 回车\r本义是光标重新回到本行开头,r的英文return,控制字符可以写成CR,即Carriage Return(回车,carriage有&q ...

  5. Java序列初始化

    1.数组 Java数组可以用元素集合初始化: char[] c=new char[]{'.','.','.','.'}; 而想要用指定数量的相同元素来初始化数组,可以使用Arrays.fill()方法 ...

  6. 第五课第四周笔记2:Self-Attention 自注意力

    Self-Attention 自注意力 让我们跳进去谈谈transformer的self-attention机制.如果您能了解本视频背后的主要思想,您就会了解变压器网络工作背后最重要的核心思想. 让我 ...

  7. 「笔记」$Min\_25$筛

    总之我也不知道这个奇怪的名字是怎么来的. \(Min\_25\)筛用来计算一类积性函数前缀和. 如果一个积性函数\(F(x)\)在质数单点是一个可以快速计算的关于此质数的多项式. 那么可以用\(Min ...

  8. MIPI归纳---为什么阻抗为100欧姆

    根据LVDS(Low Voltage Differential Signaling)电平定义的. LVDS差分信号PN两线最大幅度是350mV,内部一个恒流源电流是3.5mA.于是终端匹配电阻是100 ...

  9. HttpContext.Current.Request.Url 地址:获取域名

    假设当前页完整地址是:http://www.test.com/aaa/bbb.aspx?id=5&name=kelli 协议名----http://域名  ---- www.test.com站 ...

  10. Python ImportError: cannot import name ABC

    Python 3.5.2 测试可以运行 import sys from abc import ABC,abstractmethod class MyBase(ABC): @abstractmethod ...