news 2026/10/2 20:54:01

原型模式(Prototype Pattern)Java 实战指南:基于 java-design-patterns 的克隆创建深入解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
原型模式(Prototype Pattern)Java 实战指南:基于 java-design-patterns 的克隆创建深入解析
  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design 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 中实现原型模式,推荐的方式是创建一个带克隆方法的抽象基类。本示例中的Prototype<T>基类通过泛型化的copy()方法完成克隆(源码见 Prototype.java):

public abstract class Prototype<T> implements Cloneable { @SuppressWarnings("unchecked") @SneakyThrows public T copy() { return (T) super.clone(); } }

这里有几个关键技术点值得展开:

  • implements Cloneable:Object.clone()是受保护的原生方法,只有实现了Cloneable标记接口的类才能调用它,否则会抛出CloneNotSupportedException;
  • @SneakyThrows(Lombok):super.clone()声明抛出受检异常CloneNotSupportedException,用@SneakyThrows可以"偷偷"绕过受检异常,让copy()的调用方无需捕获,代码更简洁;
  • 泛型返回(T) super.clone():利用泛型T让copy()返回具体子类型,配合@SuppressWarnings("unchecked")消除强制转换警告,调用方无需再自行强转;
  • 默认浅克隆:Object.clone()默认执行浅拷贝(shallow copy),即基本类型字段直接复制、引用类型字段复制引用本身。这一点对理解后续行为至关重要。

第二步:构建生物类层次

以Beast(野兽)与OrcBeast(兽人野兽)为例(源码见 Beast.java 与 OrcBeast.java):

@EqualsAndHashCode(callSuper = false) @NoArgsConstructor public abstract class Beast extends Prototype<Beast> { 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通过@RequiredArgsConstructor(Lombok)生成接受weapon的构造器,同时手写了OrcBeast(OrcBeast orcBeast)拷贝构造器,逐字段复制weapon;
  • @EqualsAndHashCode(callSuper = false)确保克隆后的对象与原型值相等(equals 语义一致),这一点在测试中被直接验证;
  • 每个具体类重写toString(),形成可读的输出文本。

同样的结构还存在于Mage(Mage.java)、Warlord(Warlord.java)及其精灵/兽人实现中:

抽象基类精灵实现兽人实现状态字段
BeastElfBeast("Elven eagle helps in ...")OrcBeast("Orcish wolf attacks with ...")helpType/weapon
MageElfMageOrcMagehelpType/weapon
WarlordElfWarlordOrcWarlordhelpType/weapon

完整的类结构可以从类图源文件 prototype.urm.puml 中确认:Beast、Mage、Warlord三个抽象类分别实现Prototype,精灵/兽人具体类继承各自抽象基类,HeroFactoryImpl同时依赖并持有这三种原型对象。

第三步:用原型工厂批量生产对象

为了充分发挥原型模式的价值,仓库提供了HeroFactory接口与其实现HeroFactoryImpl(源码见 HeroFactory.java 与 HeroFactoryImpl.java):

public 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):

从类图可以清晰看到两条关键关系:

  1. Beast、Mage、Warlord均实现(..|>)Prototype,继承copy()克隆能力;
  2. 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)

根据文档与源码,在以下场景应当考虑原型模式:

  1. 待实例化的类在运行时才确定,例如通过动态加载(Class.forName等机制)获得具体类型;
  2. 希望避免构建一套与产品类层次平行的工厂类层次——原型模式用一个持有原型的工厂即可覆盖整个产品族;
  3. 类的实例只可能有少数几种状态组合——预先安装好对应数量的原型,需要时克隆即可,比每次手工new并逐字段设置状态更便捷;
  4. 对象创建成本远高于克隆成本——例如对象构造涉及重量级初始化(数据库连接、IO 资源、复杂计算)时,克隆现成实例可避免重复初始化;
  5. 具体类在运行时仍然未知(与第 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-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 20:53:55

RMUX Python与TypeScript SDK入门:librmux与@rmux/sdk自动化实战

RMUX Python与TypeScript SDK入门&#xff1a;librmux与rmux/sdk自动化实战 【免费下载链接】rmux Universal Rust multiplexer with a typed SDK — drive any CLI or TUI app from code. Native on Linux, macOS, and Windows. 项目地址: https://gitcode.com/gh_mirrors/r…

作者头像 李华
网站建设 2026/10/2 20:51:22

三进制量化实战:16GB显卡部署27B模型与llama.cpp优化指南

1. 为什么27B模型能塞进16GB显卡&#xff1a;三进制量化的底层逻辑 第一次看到"16GB显卡跑27B模型"这个说法&#xff0c;我的反应和大多数人一样——这不可能。按照常规认知&#xff0c;27B参数量的模型即便用4bit量化&#xff0c;权重占用也要接近14GB&#xff0c;再…

作者头像 李华
网站建设 2026/10/2 20:50:22

从JSON到关系图谱:a2grunnerp声明式建图实战指南

在这个万物皆可图谱化的时代&#xff0c;我越来越觉得“建图”这件事本身&#xff0c;才是最大的门槛。你以为我说的门槛是 GraphQL 或者图数据库调优&#xff1f;不是&#xff0c;是最基础的&#xff1a;接口数据明明是 JSON&#xff0c;业务关系就摆在眼前&#xff0c;可你想…

作者头像 李华