前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >漫谈模式之规格模式

漫谈模式之规格模式

作者头像
孟君
发布2023-04-08 08:15:56
2.4K0
发布2023-04-08 08:15:56
举报
文章被收录于专栏:孟君的编程札记

本文我们来介绍一下规格模式(Specification Pattern)

规格模式是一种常用的软件设计模式,其目的将业务规则封装成可重用的对象,并且能够动态地组合这些规则,以实现更复杂的业务逻辑。

规格模式的基本介绍

意图

规格模式(Specification Pattern)是一种行为设计模式,它的意图是将一个业务规则表示为一个对象,并且将多个规则组合成更为复杂的规则。该模式可以将规则的表示与规则的实现分离,从而实现可扩展性、可维护性和可重用性。

结构

规格模式的结构大致如下:

图片
图片

这里涉及到的参与者有如下几种:

Specification(规则接口)

包含一个用于检验对象是否符合规则的方法,如isSatisfiedBy

AbstractSpecification(抽象规则)

包含一个抽象的isSatisfiedBy方法,以及and、or和not方法,适合于与AND、或OR、非NOT场景的支持。

LeafSpecification(具体规则)

实现规则接口的isSatisfiedBy方法

规格模式的示例

在给出示例之前,我们先来看一个筛选的例子。

在有些场景中,需要对一个集合的对象进行过滤。比如,我有很多本书,想要知道价格为50元以上且出版社包含“工业”字样的书籍有哪些。简单实现一下:

Book类

图片
图片

简单过滤的基本逻辑:

那么问题来了,如果筛选条件很多,if条件的判断就变得很不好维护

接下来,我们使用规格模式的写法来完成,看看效果。

示例一、条件筛选

规格接口

抽象规格

图片
图片

AND、OR和Not规格

图片
图片

具体书本规格

Client端

条件的筛选我们可以通过如下方式来做

完整Client代码

运行结果

图片
图片

这样一个简单的规格模式示例就完成了。

当然,为了方便多个规则的组装,我们也可以写一个规格Builder,然后通过addSpecification()方法来增加规格。如:

规格Builder

图片
图片

Client客户端

同样,执行之后也能看到过滤的后的结果。

如果使用JDK 1.8及以上版本,使用Predicate甚至不需要写任何的Specifation即可完成。使用Predicate结合stream可以轻松使用筛选。

Predicate接口组成

查看Predicate源码,我们可以看到,其定义了一个test方法,以及and、or和negative方法,和Specification类似。

图片
图片

示例二、JPA使用规格模式构建查询语句

我们知道,Spring Data JPA是Spring框架(Spring Boot)中提供的非常有用的模块,可以以最小的工作量访问持久层并使用JPA减少大量样板代码

它允许使用不同的方法创建查询,例如:

  • 派生查询方法(从方法名称创建查询)
  • @Query注释(编写本地SQL或JPQL查询)。

除了这些常见的方法之外,还有一种方法可以使用Spring JPA已提供的Specification Pattern动态创建查询,并利用JPA Criteria API的优势。

有时候,针对一个对象需要应用不同的查询条件,需要为每种可能的组合创建大量不同的方法。Specification Pattern源自Eric Evans的《领域驱动设计》一书中介绍的概念。它提供了一种设计模式,允许我们将搜索条件与执行搜索的对象分开

注:也可以通过访问如下url地址https://martinfowler.com/apsupp/spec.pdf获取Evans和Martin Fowler所写的Specification文章。

让我们来看一个例子。

使用Spring Data JPA提供的Repository模式及其功能,通常会开始为应用程序和业务逻辑所需的每个不同查询添加新的方法定义。对于具有许多属性/字段的实体,Repository可能会以大量不同的查询组合方式结束,所有这些方法都在单独的方法中,因此我们的类会变得越来越庞大,包含数十个甚至更多。如:

从生产力的角度来看,这种情况是可以接受的,作为开发人员,我可以在几秒钟内创建一个方法,通过某些特定字段过滤数据库并返回Java中的值,我们作为开发人员将专注于功能和业务逻辑。

然而,从可读性和可维护性的角度来看,这种情况,一个包含几十个方法甚至更多方法的类简直就是一场噩梦。由于Spring Data JPA的命名约定,我们可能会有一些难以理解的方法名。

规格模式是一种很好的解决方案,它可以在代码中增强可读性和可维护性,同时最大限度地减少样板代码,并且重复使用现有代码。

在Spring Data JPA中使用规格模式

Spring已经提供了Specification接口来实现它,并使不同的规格在我们的代码库中可重用。

Specification

图片
图片

JpaSpecicationExecutor<T>

图片
图片

实现JpaSpecificationExecutor接口后,Repository类会新增一些方法,用于使用Specification进行查询。这些方法将替代我们之前需要的大量的方法来满足每个不同的条件组合。

图片
图片

在使用 JPA 的 Repository 中使用规格模式

图片
图片

这样,我们就可以轻松地创建可重用和可组合的查询规格,并将它们用于不同的查询。代码也变得清晰明了。

图片
图片

其它阅读

优缺点

优点

  • 可维护性:将业务规则进行抽象和组合,使得规则的实现与表示分离,便于业务规则的维护和更新。
  • 可扩展性:通过组合多个规则,可以实现更为复杂的规则,从而支持业务的变化和扩展。
  • 可重用性:将业务规则封装成对象,可以在不同的场景中进行复用,从而减少代码冗余。

缺点

  1. 复杂性:由于规格模式需要将业务规则抽象和组合,因此实现过程较为复杂,需要花费一定的时间和精力。
  2. 运行效率:由于规格模式需要将多个规则组合,因此可能会导致运行效率的降低。特别是在需要进行大量规则匹配的情况下,可能会影响系统的性能。

规格模式和组合模式

规格模式和组合模式是两个不同的设计模式,它们虽然有些相似之处,但并不是一个的扩展。

组合模式是一种结构型模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得客户端可以统一处理单个对象和对象组合,从而使得代码更加简洁、灵活。

规格模式则是一种行为型模式,它将业务规则封装为一个规格对象,用于判断给定对象是否满足特定的规则。规格模式可以将多个规格对象进行组合,从而得到更复杂的规则。在规格模式中,客户端通常需要将业务规则和对象进行分离,从而使得业务规则的变化不会影响到对象的实现。

其它

规格模式是领域驱动设计(DDD)中的一种模式。在DDD中,规格模式被用于描述领域中的特定概念,例如产品规格、订单规格等等。规格模式可以让我们通过将业务逻辑与数据访问逻辑分离来更好地实现领域驱动设计的目标

规格模式可以使用的场景可以有:

  • 应用筛选/搜索条件时
  • 从代码中提取业务规则
  • 执行单元测试
  • 组件/特定对象的选择
  • 构建一些复杂的解析逻辑等等

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 规格模式的基本介绍
  • 规格模式的示例
  • 其它阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档