ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java 建造者模式(Builder Pattern)实战:以 java-design-patterns 项目 Hero 构建为例

Java 建造者模式(Builder Pattern)实战:以 java-design-patterns 项目 Hero 构建为例 示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载建造者模式Builder Pattern是 GoF 23 种经典创建型模式之一核心思想是把复杂对象的构造过程与最终表示分离让同一套构造流程能够产出不同的对象形态。本文以开源仓库 java-design-patterns 的builder模块为蓝本从伸缩构造器反模式问题出发逐行剖析Hero与其内部Builder的源码实现、流式fluent调用链、参数约束与测试验证读完即可在自己的 Java 项目中落地一套可复制的建造者方案。目的将复杂对象的构造与其表示分开建造者模式的目的非常明确将复杂对象的构造与其表示分开以便同一构造过程可以创建不同的表示。在builder模块中这一目的被完整落地为Hero产品与Hero.Builder建造者两个角色二者共同位于 Hero.java。在动手写代码之前先理解它要解决的问题。它要解决的核心问题伸缩构造器反模式Telescoping Constructor Antipattern维基百科对建造者模式的定义是建造者模式是一种对象创建的软件设计模式旨在为伸缩构造器反模式寻找一个解决方案。所谓伸缩构造器反模式指的就是下面这类构造函数——参数越来越多、排列顺序越来越难理解public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) { }从 App.java 的类注释可以看出该模式引入的背景正是对象构造参数的组合增加会导致构造函数数量呈指数级膨胀an exponential list of constructors。而建造者模式的做法是不再堆叠构造函数而是引入一个独立的 builder 对象让它逐步接收每一个初始化参数最后一次性返回构造完成的对象。此外注释中还指出了建造者模式的另一大用途对于包含扁平数据的对象——例如 HTML 代码、SQL 查询、X.509 证书等无法逐段编辑、只能一次性组装的数据builder 是构建这类对象的最佳方式。现实世界例子想象一个角色扮演游戏的角色生成器。最简单的选择是让计算机为你创建角色。但是如果你想选择一些像专业、性别、发色等角色细节时这个角色生成就变成了一个渐进的过程。当所有选择完成时该过程也将完成。用通俗的话说建造者模式允许你创建不同口味的对象同时避免构造器污染。当一个对象可能有几种口味或者一个对象的创建涉及到很多步骤时会很有用。下面这张类图来自仓库的builder/etc目录展示了Hero、Builder与各属性类型之间的关系编程示例从 Hero 到 Hero.Builder 的完整实现第一步产品类 Hero在仓库当前实现中Hero是一个recordJava 16由六个字段构成public record Hero( Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) { private Hero(Builder builder) { this( builder.profession, builder.name, builder.hairType, builder.hairColor, builder.armor, builder.weapon); } // ... }关键点Hero的公开构造路径只有一条——经由Builder的私有构造函数外部无法绕过 builder 直接 new六个字段中profession与name是构建时的必填项其余四项均为可选Hero本身不可变record 的字段天然 final构建完成后任何属性都无法再修改这保证了对象的线程安全与可安全发布。第二步属性枚举profession、发色、发型、护甲、武器五类属性分别由五个枚举承载枚举可选值文件ProfessionWARRIOR、THIEF、MAGE、PRIESTProfession.javaHairColorWHITE、BLOND、RED、BROWN、BLACKHairColor.javaHairTypeBALD、SHORT、CURLY、LONG_STRAIGHT、LONG_CURLYHairType.javaArmorCLOTHES、LEATHER、CHAIN_MAIL、PLATE_MAILArmor.javaWeaponDAGGER、SWORD、AXE、WARHAMMER、BOWWeapon.java其中HairType与Armor通过 Lombok 的AllArgsConstructor注入展示用文案如LONG_CURLY→ long curly并重写toString()返回小写/友好文本以便最终输出可读的角色描述。第三步建造者 BuilderBuilder是Hero的静态内部类遵循Joshua Bloch 在《Effective Java》中描述的 Builder 变体这一点在 App.java 注释中有明确说明。核心机制有三个构造函数只收必填参数并做防御性校验public Builder(Profession profession, String name) { if (profession null || name null) { throw new IllegalArgumentException(profession and name can not be null); } this.profession profession; this.name name; }可选参数通过流式方法fluent interface逐步设置每个方法返回this支持链式调用public Builder withHairType(HairType hairType) { this.hairType hairType; return this; } public Builder withHairColor(HairColor hairColor) { this.hairColor hairColor; return this; } public Builder withArmor(Armor armor) { this.armor armor; return this; } public Builder withWeapon(Weapon weapon) { this.weapon weapon; return this; }build()作为终点一次性产出不可变产品public Hero build() { return new Hero(this); }第四步实际使用在 App.java 的main方法中同一套构建流程被用来创建三种截然不同的角色var mage new Hero.Builder(Profession.MAGE, Riobard) .withHairColor(HairColor.BLACK) .withWeapon(Weapon.DAGGER) .build(); LOGGER.info(mage.toString()); var warrior new Hero.Builder(Profession.WARRIOR, Amberjill) .withHairColor(HairColor.BLOND) .withHairType(HairType.LONG_CURLY) .withArmor(Armor.CHAIN_MAIL) .withWeapon(Weapon.SWORD) .build(); LOGGER.info(warrior.toString()); var thief new Hero.Builder(Profession.THIEF, Desmond) .withHairType(HairType.BALD) .withWeapon(Weapon.BOW) .build(); LOGGER.info(thief.toString());注意三个角色的差异mage只设置发色与武器warrior使用了全部四个可选参数thief甚至没有设置发色和护甲——这正是同一构造过程可以创建不同表示的直接体现同时完全绕开了参数顺序易错的构造器地狱。实际运行输出由Hero.toString()生成逻辑见 Hero.java 第 46-69 行This is a mage named Riobard with black hair and wielding a dagger. This is a warrior named Amberjill with blond long curly hair wearing chain mail and wielding a sword. This is a thief named Desmond with bald head and wielding a bow.toString()的实现本身也演示了如何优雅处理可选字段为null的情况如未设置发型时只输出发色BALD时输出 head 而非 hair。如何运行本示例模块采用 Maven 管理仓库根目录提供了 Maven Wrapper可以在仓库根目录直接运行./mvnw -pl builder test # 运行 builder 模块的全部测试 ./mvnw -pl builder exec:java # 或直接运行主程序查看输出运行主程序后控制台会打印出上面三行角色描述日志。源码佐证测试用例如何验证 Builder 行为builder模块的测试位于 builder/src/test/java/com/iluwatar/builder覆盖了建造者的两个关键约束HeroTest.java 的testMissingProfession与testMissingName分别以null职业、null姓名构造Hero.Builder断言抛出IllegalArgumentException验证必填参数的防御性校验确实生效同文件testBuildHero通过完整链式调用构建一个WARRIOR英雄再逐字段断言profession()、name()、armor()、weapon()、hairType()、hairColor()与设置值一致验证 builder 的参数传递正确性AppTest.java直接执行App.main断言整个示例程序可无异常跑通。适用性什么时候应该使用建造者模式原文档明确指出在以下场景使用建造者模式创建复杂对象的算法应独立于组成对象的零件及其组装方式构造过程必须允许所构造的对象具有不同的表示形式当一个产品需要很多步骤才能创建完成且这些步骤需要按特定顺序执行时该模式尤其适用。换句话说产品本身复杂 存在多种可选组合是使用 Builder 的两大信号。优点与权衡优点相比其他创建型模式对构造过程拥有更细粒度的控制支持分步构造对象可以延后某些构造步骤甚至递归地执行步骤能够构造需要复杂组装子对象的对象最终产品与其组成部分及组装过程解耦遵循单一职责原则把复杂的构造代码从产品的业务逻辑中隔离出来。权衡由于需要额外创建 builder 类整体代码复杂度会有所上升需要创建多个 builder 对象可能带来一定的内存开销。Java 世界中的真实应用建造者模式在 Java 生态中随处可见最常见的实例包括java.lang.StringBuilder/java.lang.StringBuffer链式append逐步拼装字符串java.nio.ByteBuffer及同类缓冲FloatBuffer、IntBuffer等java.lang.Appendable的全部实现类javax.swing.GroupLayout.Group#addComponent()式的 GUI 组件逐步组装各类 IDE 内置的 UI 构建器Apache Camel 的 builder 包、Apache Commons CLI 的Option.Builder等框架级实现。与相关设计模式的关系抽象工厂Abstract Factory可以与建造者配合使用由抽象工厂生产复杂对象的各个组成部分再由 Builder 完成最终组装原型PrototypeBuilder 常基于一个原型对象来创建新对象步骤建造者Step Builder是 Builder 的一种变体采用分步式 API 构造复杂对象。当对象拥有大量可选参数、又希望彻底规避伸缩构造器反模式时Step Builder 是更优选择仓库中同样有独立的 step-builder 模块可供对照学习。参考与致谢本模块的实现思路与文档内容参考了以下经典著作《Design Patterns: Elements of Reusable Object-Oriented Software》GoF 原书《Effective Java》Joshua BlochBuilder 变体的出处《Head First Design Patterns: A Brain-Friendly Guide》《Refactoring to Patterns》小结回顾整个builder模块必填参数进构造器、可选参数走流式方法、build()一次性产出不可变对象——这套骨架完整诠释了将构造过程与表示分离的模式意图。结合 Hero.java 的 record 实现、App.java 的多角色演示以及 HeroTest.java 的参数校验测试你可以在自己的项目中直接套用同一套模式用可读、可扩展、参数安全的 API 取代臃肿的构造函数。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐Java Builder 模式实战以 java-design-patterns 的 Hero 构建为例彻底告别构造器污染Java Builder 模式实战以 java design patterns 的 Hero 构建为例彻底告别构造器污染 Builder建造者模式是 Go示例工程教程Java 设计模式之 Builder建造者模式java-design-patterns 仓库中的 Hero 构建实战Java 设计模式之 Builder建造者模式java design patterns 仓库中的 Hero 构建实战 Builder建造者模式是 Ja示例工程教程使用 Java Builder 模式构建复杂对象以 java-design-patterns 仓库的 Hero 示例为例使用 Java Builder 模式构建复杂对象以 java design patterns 仓库的 Hero 示例为例 导读 Builder建造者是 G示例工程教程上一篇RIOT OS 测试应用深度解析Atmel IO1 Xplained 扩展板的驱动测试程序下一篇OmO 原生遥测本地技能查询库扩展实战基于 HogQL 构建工具并行度汇总查询parallelism / parallelism_daily创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表