示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载导读本文以 java-design-patterns 仓库中的 Specification 模块 为蓝本系统讲解 Specification规范模式的核心思想、三种实现形态硬编码、参数化、组合式及其源码级实现。通过阅读本文你将掌握如何把筛选规则与被筛选对象解耦并利用and/or/not三个布尔操作符将原子规则组合成任意复杂的业务查询同时理解该模式在数据访问Data access场景下与 Repository 模式的协作关系。模式概述别名También conocido comoSpecification 模式通常也被称为Filter过滤器或Criteria条件这从侧面说明了它的本质一种可复用的筛选条件抽象。目的PropósitoSpecification 模式的核心目的是把如何匹配一个候选对象的声明与被匹配的候选对象本身分离开来。除了用于集合筛选selection该模式在以下两类场景中同样极具价值校验validation检查某个对象是否满足特定业务规则从而决定它是否适合承担某项功能按需构建building to order依据规格动态构造符合条件的对象或查询。这种解耦带来的直接收益是业务规则可以被命名、被复用、被组合而不是散落在if/else或硬编码过滤逻辑中。模式分类在仓库目录结构上Specification 属于Behavioral行为型模式同时带有Data access数据访问标签——这提示了它最常见的应用场景作为数据查询与筛选规则的抽象层。为什么需要 Specification一个选龙的现实问题设想你面对一群属性各异的生物需要从中选出某个子集。筛选条件可能是能飞的生物体重超过 500 千克的生物或上述条件的任意布尔组合比如红色的、会飞的、且不是小体型如果用传统的硬编码方式每新增一种组合就要新增一段过滤逻辑或一个新类代码会迅速膨胀。Specification 模式的思路是把每个最小的筛选条件封装成一个独立的选择器Selector再用布尔逻辑把它们自由拼接——正如 Wikipedia 对该模式的描述业务规则可以通过布尔逻辑链式重组合并。用一句简单的话概括Specification 模式让我们把搜索条件和执行搜索的对象分离。领域模型生物Creature及其属性仓库中的示例以生物池为领域模型代码位于 creature 包 与 property 包。所有生物实现统一的Creature接口public interface Creature { String getName(); Size getSize(); Movement getMovement(); Color getColor(); Mass getMass(); }接口中的属性分为两类对应文档中提到的两种取值形态属性类型取值形态size枚举Size预定义有限集合SMALL/NORMAL/LARGESize.javamovement枚举Movement预定义有限集合WALKING/SWIMMING/FLYINGMovement.javacolor枚举Color预定义有限集合DARK/LIGHT/GREEN/REDColor.javamass值对象Mass连续值在实例化时以参数传入如39300.0千克其中Mass是值得注意的连续值属性Mass.java。它封装了double数值并提供了一组比较方法供后续选择器复用public final boolean greaterThan(Mass other) { return this.value other.value; } public final boolean smallerThan(Mass other) { return this.value other.value; } public final boolean greaterThanOrEq(Mass other) { return this.value other.value; } public final boolean smallerThanOrEq(Mass other) { return this.value other.value; }AbstractCreatureAbstractCreature.java是各生物实体的公共基类负责保存全部属性并提供toString()格式化输出格式为Dragon [sizelarge, movementflying, colorred, mass39300.0kg]。以文档中的Dragon为例Dragon.javapublic class Dragon extends AbstractCreature { public Dragon() { this(new Mass(39300.0)); } public Dragon(Mass mass) { super(Dragon, Size.LARGE, Movement.FLYING, Color.RED, mass); } }仓库中共有 6 种生物实体Goblin、Octopus、Dragon、Shark、Troll、KillerBee见 App.java 的初始化列表它们构成了筛选演示的数据池。三种筛选规则形态硬编码、参数化与组合式Specification 的灵活性来自它对规则的三种抽象层次。下面逐一结合源码展开。形态一硬编码规范Hard-coded Specification当筛选条件数量少且预先已知时可以把条件直接写死在选择器类中。典型代表是MovementSelectorMovementSelector.javapublic class MovementSelector extends AbstractSelectorCreature { private final Movement movement; public MovementSelector(Movement m) { this.movement m; } Override public boolean test(Creature t) { return t.getMovement().equals(movement); } }同类选择器还有 ColorSelector.java 与 SizeSelector.java它们分别针对Color、Size这两个枚举属性做等值判断。使用方式非常直接——配合 Java Stream API 完成筛选var redCreatures creatures.stream().filter(new ColorSelector(Color.RED)) .collect(Collectors.toList());值得注意AbstractSelectorT实现了java.util.function.PredicateT因此选择器天然可以作为Stream.filter()的参数与函数式编程无缝衔接见 AbstractSelector.java。形态二参数化规范Parameterized Specification当筛选条件覆盖较宽的取值范围如质量、速度等连续量时枚举型硬编码就力不从心了。此时把阈值作为构造参数传入实例化时再绑定从而获得灵活性。MassGreaterThanSelector是文档中的典型示例MassGreaterThanSelector.javapublic class MassGreaterThanSelector extends AbstractSelectorCreature { private final Mass mass; public MassGreaterThanSelector(double mass) { this.mass new Mass(mass); } Override public boolean test(Creature t) { return t.getMass().greaterThan(mass); } }参数化的阈值在test()中通过Mass.greaterThan()与候选对象的实际质量比较var heavyCreatures creatures.stream().filter(new MassGreaterThanSelector(500.0)) .collect(Collectors.toList());同一组里还提供了 MassEqualSelector.java等值判断与 MassSmallerThanOrEqSelector.java小于或等于分别映射到Mass上的greaterThanOrEq与smallerThanOrEq等比较方法构成完整的质量区间查询能力。形态三组合式规范Composite Specification——三种布尔操作符当存在大量可多种方式组合的筛选条件时为每一种组合创建一个类变得不切实际。组合式规范应运而生用and、or、not三个布尔操作符把叶子选择器动态拼装成任意复杂规则。这一切的基石是抽象基类AbstractSelectorTAbstractSelector.java它实现了PredicateT并定义了三个组合方法public abstract class AbstractSelectorT implements PredicateT { public AbstractSelectorT and(AbstractSelectorT other) { return new ConjunctionSelector(this, other); } public AbstractSelectorT or(AbstractSelectorT other) { return new DisjunctionSelector(this, other); } public AbstractSelectorT not() { return new NegationSelector(this); } }三个组合操作符分别由三个组合选择器类实现1. 合取AND——ConjunctionSelectorConjunctionSelector.java要求所有叶子选择器都通过测试。public class ConjunctionSelectorT extends AbstractSelectorT { private final ListAbstractSelectorT leafComponents; SafeVarargs ConjunctionSelector(AbstractSelectorT... selectors) { this.leafComponents List.of(selectors); } /** Tests if *all* selectors pass the test. */ Override public boolean test(T t) { return leafComponents.stream().allMatch(comp - (comp.test(t))); } }2. 析取OR——DisjunctionSelectorDisjunctionSelector.java要求至少一个叶子选择器通过测试内部使用anyMatch。3. 否定NOT——NegationSelectorNegationSelector.java对单个选择器的结果取反。它单独使用意义不大通常与其他组合选择器搭配public class NegationSelectorT extends AbstractSelectorT { private final AbstractSelectorT component; NegationSelector(AbstractSelectorT selector) { this.component selector; } /** Tests if the selector fails the test (yes). */ Override public boolean test(T t) { return !(component.test(t)); } }有了这三个操作符就可以把特殊的生物定义为红色、会飞、且不是小体型写成一行式规则var specialCreaturesSelector new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING)).and(new SizeSelector(Size.SMALL).not()); var specialCreatures creatures.stream().filter(specialCreaturesSelector) .collect(Collectors.toList());组合使用的注意事项组合多个布尔操作符时极易出错尤其要小心运算优先级。and合取与or析取在 Java 中会按照调用链从左到右执行方法调用本身就是顺序的因此请像写复合布尔表达式一样仔细确认每层括号所表达的语义。组合式规范的核心价值在于可复用性不必为每次过滤操作都创建新的 Selector 类只需在运行时用通用的叶子选择器加基本布尔逻辑即时拼装出AbstractSelector实例。三种形态对比文档中给出了清晰的权衡表这里完整保留形态适用场景优点缺点硬编码规范筛选条件少且预先已知 易于实现 表达力强- 不灵活参数化规范筛选条件覆盖较宽的取值范围如质量、速度…… 具有一定灵活性- 仍需要专用的特殊目的类组合式规范存在大量可按多种方式组合的筛选条件为每个选择器建类不可行 非常灵活无需大量专用类 支持逻辑运算- 稍难理解- 仍需先创建作为叶子的基础类端到端演示App 主程序完整的运行示例位于 App.java它把三种形态串成一条主线。主程序先用 6 种生物构造数据池再依次演示硬编码演示查找所有行走的生物new MovementSelector(Movement.WALKING)查找所有深色的生物new ColorSelector(Color.DARK)参数化演示查找所有重于 600kg 的生物new MassGreaterThanSelector(600.0)查找所有轻于或恰好 500kg 的生物new MassSmallerThanOrEqSelector(500.0)组合式演示查找所有红色且会飞的生物var redAndFlying new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING));查找所有可怕的生物深色或红色、非游泳、且质量大于等于 400kg——这是一个同时使用三种操作符的复杂规则var scaryCreaturesSelector new ColorSelector(Color.DARK) .or(new ColorSelector(Color.RED)) .and(new MovementSelector(Movement.SWIMMING).not()) .and(new MassGreaterThanSelector(400.0).or(new MassEqualSelector(400.0))); print(creatures, scaryCreaturesSelector);其中print方法接收的是PredicateCreature而非具体的 Selector 类型——这再次印证了设计意图调用方只依赖函数式接口契约不依赖选择器的具体实现private static void print(List? extends Creature creatures, PredicateCreature selector) { creatures.stream().filter(selector).map(Objects::toString).forEach(LOGGER::info); }运行方式在仓库根目录执行./mvnw -pl specification compile exec:java或先./mvnw -pl specification install后在 IDE 中运行App的main方法即可在日志中看到各类筛选的命中结果。测试验证组合逻辑的正确性由测试保障组合式规范的正确性有专门的单元测试背书——CompositeSelectorsTest.java 使用 Mockito 构造模拟生物分别验证三种操作符testAndComposition轻于50kg且会游泳的组合对游泳且重 100kg的生物返回false对游泳且轻 25kg的生物返回true——证明 AND 组合要求全部条件成立testOrComposition轻于50kg或会游泳的组合对两种模拟生物都返回true——证明 OR 组合只要任一条件成立即可testNotComposition不轻于50kg即not()取反的组合对重生物返回true、对轻生物返回false——证明 NOT 正确反转结果。仓库还提供了ColorSelectorTest、MassSelectorTest、MovementSelectorTest、SizeSelectorTestselector 测试目录以及 CreatureTest.java分别覆盖各叶子选择器与生物实体的行为。这套测试既是回归保障也充当了组合式规范语义的可执行文档。类图与结构速览以下是本模式的结构类图由 specification.ucls 生成从类图与源码目录可以清晰看到模块的分层领域层creatureCreature接口、AbstractCreature基类、6 种生物实体与 property3 个枚举 Mass值对象规则层selectorAbstractSelector抽象基类、8 个叶子选择器、3 个组合选择器入口层app/App.java演示程序。仓库中另有一张 流程图直观展示了从候选生物到选择器判定、再到结果集输出的完整流程可作为理解数据流的辅助参考。何时使用 Specification 模式根据文档与源码在以下两种场景中应当考虑引入 Specification 模式需要基于某些标准选择对象子集并且要在不同时刻刷新筛选结果——例如后台任务中反复按动态条件查询数据这正是组合式规范配合 Stream 过滤发挥最大价值的场景。需要校验只有合适的对象才能被用于特定功能——即把业务规则本身当作可命名、可组合、可单测的一等公民。相关模式Specification 与Repository仓库模式关系最为密切Repository 负责封装数据访问与持久化而 Specification 为其提供可组合的查询条件二者配合可以实现把查询意图从数据访问实现中彻底剥离的领域驱动设计风格。本仓库的 repository 模块 可作为延伸阅读。延伸阅读与参考本模式的完整设计与权衡论述源自Martin Fowler 的《Specifications》论文文档的 Créditos致谢一节明确标注了该出处想在一个真实、可运行的 Java 项目中继续研究该模式可通读 specification 模块 的源码、pom.xml 与上述测试代码该模式在仓库中被标记为Data access类别说明它常与数据访问层配合出现建议结合 Repository 与 Data access object 等模块一起研读形成完整的数据访问模式图谱。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐Comp AI CRM 中 React 组合模式规则模板规范用结构化规则库根治布尔属性爆炸Comp AI CRM 中 React 组合模式规则模板规范用结构化规则库根治布尔属性爆炸 本文围绕 Comp AI CRM 仓库中 .agents/skil后端前端CRM人工智能AI AgentEasy Rules规则组合CompositeRule实现复杂业务逻辑编排Easy Rules规则组合CompositeRule实现复杂业务逻辑编排 Easy Rules是一个简单易用的Java规则引擎通过CompositeRul后端ASP.NET Boilerplate 规范模式Specification Pattern完全指南用可组合、可复用的业务过滤器替代散落的查询表达式ASP.NET Boilerplate 规范模式Specification Pattern完全指南用可组合、可复用的业务过滤器替代散落的查询表达式 规范模后端Web框架依赖注入认证鉴权上一篇Paperless-ngx 多语言部署指南3 步配好中文界面、OCR 识别与日期解析下一篇别再手工盘点了.NET 开源 WMS 十分钟跑起来创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考