Java 实战指南:基于 java-design-patterns 的克隆创建深入解析)
示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载原型模式Prototype Pattern是一种创建型设计模式其核心思想是用一个原型实例来指定待创建对象的类型并通过克隆Clone而不是new来产生新对象。在 java-design-patterns 仓库中prototype模块用一套完整的兽人/精灵军团示例演示了这一模式从抽象基类、克隆工厂到测试验证的完整落地方式。读完本文你将掌握Object.clone()的原生克隆机制、如何用泛型抽象copy()方法、如何通过原型工厂批量生成对象以及浅克隆与深克隆的取舍。模式意图与适用场景原型模式的意图可以用一句话概括通过一个原型实例指定要创建的对象类型并通过复制这个原型来创建新对象。它属于 GoF 提出的创建型设计模式之一在 prototype/README.md 中被标注为Creational分类并带有Gang Of Four与Instantiation两个标签。Wikipedia 的定义与本仓库文档一致原型模式是一种创建型设计模式当需要创建的对象类型由一个原型实例决定时就克隆该原型以产生新对象。用更朴素的话说基于一个已存在的对象通过克隆来创建新对象。它允许你复制一个已有对象再按需修改它而不是从零开始创建并手工配置一个对象。需要注意的是原型模式并非为了性能优化而设计——文档中特别强调它不是用来获取性能收益的而仅仅用于从原型实例创建新对象。现实世界的类比文档给出了一个广为流传的例子克隆羊多莉Dolly。多莉是克隆技术的产物原型模式的要点与之完全一致——一切都是关于克隆。其他典型类比还包括定制家具工厂工厂保留最受欢迎设计的原型客户下单时直接克隆原型并做必要的定制而不是从零开始打造每件家具从而在保证质量一致性的同时快速交付游戏开发批量创建属性相似的小兵、敌人对象如 prototype/README.md 所述GUI 库用原型创建按钮、窗口等控件。程序化示例用克隆生产精灵与兽人本仓库的prototype模块位于 prototype 目录包结构为com.iluwatar.prototype。整个示例包含三套类层次Mage法师、Warlord warlord 首领与Beast野兽每套又分别有精灵Elf与兽人Orc两种实现。第一步抽象克隆接口在 Java 中实现原型模式推荐的方式是创建一个带克隆方法的抽象基类。本示例中的PrototypeT基类通过泛型化的copy()方法完成克隆源码见 Prototype.javapublic abstract class PrototypeT implements Cloneable { SuppressWarnings(unchecked) SneakyThrows public T copy() { return (T) super.clone(); } }这里有几个关键技术点值得展开implements CloneableObject.clone()是受保护的原生方法只有实现了Cloneable标记接口的类才能调用它否则会抛出CloneNotSupportedExceptionSneakyThrowsLomboksuper.clone()声明抛出受检异常CloneNotSupportedException用SneakyThrows可以偷偷绕过受检异常让copy()的调用方无需捕获代码更简洁泛型返回(T) super.clone()利用泛型T让copy()返回具体子类型配合SuppressWarnings(unchecked)消除强制转换警告调用方无需再自行强转默认浅克隆Object.clone()默认执行浅拷贝shallow copy即基本类型字段直接复制、引用类型字段复制引用本身。这一点对理解后续行为至关重要。第二步构建生物类层次以Beast野兽与OrcBeast兽人野兽为例源码见 Beast.java 与 OrcBeast.javaEqualsAndHashCode(callSuper false) NoArgsConstructor public abstract class Beast extends PrototypeBeast { public Beast(Beast source) { } }EqualsAndHashCode(callSuper false) RequiredArgsConstructor public class OrcBeast extends Beast { private final String weapon; public OrcBeast(OrcBeast orcBeast) { super(orcBeast); this.weapon orcBeast.weapon; } Override public String toString() { return Orcish wolf attacks with weapon; } }值得注意的设计细节抽象基类Beast提供了一个拷贝构造函数Beast(Beast source)虽然当前为空实现但它为子类提供了基于已有对象构造新对象的语义入口——这是原型模式中常见的深克隆备用路径OrcBeast通过RequiredArgsConstructorLombok生成接受weapon的构造器同时手写了OrcBeast(OrcBeast orcBeast)拷贝构造器逐字段复制weaponEqualsAndHashCode(callSuper false)确保克隆后的对象与原型值相等equals 语义一致这一点在测试中被直接验证每个具体类重写toString()形成可读的输出文本。同样的结构还存在于MageMage.java、WarlordWarlord.java及其精灵/兽人实现中抽象基类精灵实现兽人实现状态字段BeastElfBeastElven eagle helps in ...OrcBeastOrcish wolf attacks with ...helpType/weaponMageElfMageOrcMagehelpType/weaponWarlordElfWarlordOrcWarlordhelpType/weapon完整的类结构可以从类图源文件 prototype.urm.puml 中确认Beast、Mage、Warlord三个抽象类分别实现Prototype精灵/兽人具体类继承各自抽象基类HeroFactoryImpl同时依赖并持有这三种原型对象。第三步用原型工厂批量生产对象为了充分发挥原型模式的价值仓库提供了HeroFactory接口与其实现HeroFactoryImpl源码见 HeroFactory.java 与 HeroFactoryImpl.javapublic interface HeroFactory { Mage createMage(); Warlord createWarlord(); Beast createBeast(); }RequiredArgsConstructor public class HeroFactoryImpl implements HeroFactory { private final Mage mage; private final Warlord warlord; private final Beast beast; public Mage createMage() { return mage.copy(); } public Warlord createWarlord() { return warlord.copy(); } public Beast createBeast() { return beast.copy(); } }工厂的三个字段mage、warlord、beast就是原型对象通过RequiredArgsConstructor由构造器注入。每次调用createXxx()都执行prototype.copy()即克隆原型并返回一个独立的新实例。这正是原型模式相对抽象工厂模式的差异所在抽象工厂模式通过工厂方法创建新对象原型模式通过克隆原型实例产生新对象——客户端完全不需要知道具体类名也不知道对象是如何被构造的。第四步完整运行示例入口程序 App.java 展示了完整流程——先构造一组精灵原型再替换为一组兽人原型然后分别克隆出法师、首领和野兽public static void main(String[] args) { var factory new HeroFactoryImpl( new ElfMage(cooking), new ElfWarlord(cleaning), new ElfBeast(protecting) ); var mage factory.createMage(); var warlord factory.createWarlord(); var beast factory.createBeast(); LOGGER.info(mage.toString()); LOGGER.info(warlord.toString()); LOGGER.info(beast.toString()); factory new HeroFactoryImpl( new OrcMage(axe), new OrcWarlord(sword), new OrcBeast(laser) ); mage factory.createMage(); warlord factory.createWarlord(); beast factory.createBeast(); LOGGER.info(mage.toString()); LOGGER.info(warlord.toString()); LOGGER.info(beast.toString()); }运行该示例的控制台输出如下Elven mage helps in cooking Elven warlord helps in cleaning Elven eagle helps in protecting Orcish mage attacks with axe Orcish warlord attacks with sword Orcish wolf attacks with laser可以看到换一套原型对象工厂立即能产出完全不同种族和能力的生物——客户端的创建逻辑完全没有变化这正是原型模式面向原型编程的威力。类图与序列图下图展示了prototype模块的完整类结构包括Prototype抽象克隆基类、三个生物抽象类、六个具体实现类以及HeroFactory/HeroFactoryImpl工厂层次图片来源 prototype/etc/prototype.urm.png从类图可以清晰看到两条关键关系Beast、Mage、Warlord均实现..|Prototype继承copy()克隆能力HeroFactoryImpl组合--持有mage、warlord、beast三个原型字段并实现HeroFactory接口。时序上工厂调用链为客户端调用HeroFactoryImpl.createMage()→ 委托mage.copy()→ 内部执行super.clone()返回新实例 → 返回给客户端。完整时序可参考 prototype/etc/prototype-sequence-diagram.png。测试验证克隆的正确性由 JUnit 保障仓库为原型模式提供了参数化单元测试 PrototypeTest.java用 6 组3 兽人 3 精灵原型对象验证克隆的四个关键性质ParameterizedTest MethodSource(dataProvider) void testPrototype(P testedPrototype, String expectedToString) { assertEquals(expectedToString, testedPrototype.toString()); final var clone testedPrototype.copy(); assertNotNull(clone); // 克隆结果非空 assertNotSame(clone, testedPrototype); // 是全新对象不同引用 assertSame(testedPrototype.getClass(), clone.getClass()); // 类型一致 assertEquals(clone, testedPrototype); // 值相等equals }这个测试精确地定义了原型克隆的语义边界assertNotSame克隆对象与原型不是同一个引用不同对象assertSame类克隆对象与原型属于同一具体类运行时类型不变assertEquals克隆对象与原型值相等——这正是基类上用EqualsAndHashCode(callSuper false)的意义所在Lombok 生成的equals保证按字段比较两个对象内容相同。此外 AppTest.java 负责验证main入口可正常执行。何时使用原型模式Applicability根据文档与源码在以下场景应当考虑原型模式待实例化的类在运行时才确定例如通过动态加载Class.forName等机制获得具体类型希望避免构建一套与产品类层次平行的工厂类层次——原型模式用一个持有原型的工厂即可覆盖整个产品族类的实例只可能有少数几种状态组合——预先安装好对应数量的原型需要时克隆即可比每次手工new并逐字段设置状态更便捷对象创建成本远高于克隆成本——例如对象构造涉及重量级初始化数据库连接、IO 资源、复杂计算时克隆现成实例可避免重复初始化具体类在运行时仍然未知与第 1 点互为补充。已知应用与相关知识原型模式最经典的 Java 原生体现就是java.lang.Object#clone()方法。本仓库的Prototype.copy()本质上就是对该方法的封装与泛型化。围绕克隆机制还有几个需要明确的实践知识浅克隆 vs 深克隆Object.clone()默认浅克隆若对象图中含有可变引用字段浅克隆会让克隆体与原型共享同一引用可能引发副作用需要深克隆时通常借助拷贝构造函数或序列化等方式逐层复制。这也是原型模式实现中最容易出错、最复杂的部分Cloneable标记接口不实现它而调用clone()会抛出CloneNotSupportedException规避受检异常仓库用 Lombok 的SneakyThrows优雅处理若不用 Lombok 则需自行try/catch或将异常抛出。模式收益与代价收益隐藏了实例化新对象的复杂过程客户端只面向copy()/工厂方法减少系统中类的数量——无需为每种产品配备专属工厂类支持在运行时动态地增加和移除对象类型。代价必须实现克隆机制复杂对象图的克隆实现难度较高深克隆难以正确实现尤其当类拥有包含循环引用的复杂对象图时。与其他设计模式的关系抽象工厂模式两者都涉及对象创建但抽象工厂通过工厂方法创建对象原型模式则通过克隆原型实例创建对象二者可以互补原型可作为抽象工厂的实现手段单例模式若允许克隆单例实例单例可以利用原型来产生实例组合模式原型常被用于组合结构中以便动态创建组件树。如何运行示例prototype模块是 Maven 多模块项目java-design-patterns下的独立子模块见 prototype/pom.xml其maven-assembly-plugin已配置com.iluwatar.prototype.App作为主类。在仓库根目录运行./mvnw -pl prototype compile exec:java或使用 Maven Assembly 打包后运行./mvnw -pl prototype package java -jar prototype/target/prototype-*.jar运行后将看到上文展示的 6 行日志输出。整个示例仅依赖slf4j-api、logback-classic日志与junit-jupiter测试无其他框架负担非常适合作为理解原型模式的入门范本。赞分享示例工程教程【免费下载链接】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构建器模式是 Gan示例工程教程Java 回调模式Callback Pattern详解基于 java-design-patterns 的异步通信实现指南Java 回调模式Callback Pattern详解基于 java design patterns 的异步通信实现指南 本文以 java design示例工程教程Druid SQL 解析器重构实录统一 DialectFeature 门控模式与 tableAlias() 方法分解Druid SQL 解析器重构实录统一 DialectFeature 门控模式与 tableAlias 方法分解 导读 本文以 Druid阿里云 DataW示例工程教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考