> 技术文档 > 设计模式(五)创建型:原型模式详解

设计模式(五)创建型:原型模式详解


设计模式(五)创建型:原型模式详解

原型模式(Prototype Pattern)是 GoF 23 种设计模式中的创建型模式之一,其核心价值在于通过复制现有对象来创建新对象,而不是通过 new 关键字调用构造函数。它特别适用于对象创建成本高、构造复杂或运行时动态决定类型扩展的场景。原型模式通过克隆机制规避了昂贵的初始化过程,提升了性能,并支持在不依赖具体类的情况下动态生成对象,是实现对象复用与运行时灵活性的重要手段。在配置管理、游戏开发、文档模板系统、对象池等场景中具有广泛应用。

一、原型模式详细介绍

原型模式解决的是“对象创建效率”与“运行时灵活性”的问题。当一个对象的创建过程涉及复杂的数据加载、资源分配或计算逻辑时,频繁使用构造函数会导致性能瓶颈。原型模式通过“克隆”一个已存在的实例(即“原型”)来快速生成新对象,避免重复执行初始化逻辑。

该模式涉及以下核心角色:

  • Prototype(原型接口):声明一个克隆(clone)方法,用于返回当前对象的副本。通常在 Java 中通过实现 Cloneable 接口并重写 Object.clone() 方法实现。
  • ConcretePrototype(具体原型类):实现 Prototype 接口,提供具体的克隆逻辑。它定义了如何复制自身的状态。
  • Client(客户端):持有对 Prototype 的引用,通过调用 clone() 方法而非构造函数来创建新对象。

原型模式的关键在于克隆的深度

  • 浅克隆(Shallow Clone):仅复制对象本身及其基本类型字段,对于引用类型字段,只复制引用地址,不复制被引用的对象。Java 默认的 Object.clone() 实现即为浅克隆。
  • 深克隆(Deep Clone):不仅复制对象本身,还递归复制其所有引用对象,确保新对象与原对象完全独立,互不影响。

选择浅克隆还是深克隆取决于业务需求:若对象包含共享状态或大型资源(如缓存、连接池),浅克隆可节省内存;若要求对象完全独立,则需深克隆。

与“工厂模式”相比,原型模式不依赖类的构造逻辑,而是基于现有实例进行复制,因此更适合运行时动态配置对象的场景。例如,系统启动时加载一个“默认配置”对象作为原型,后续所有新配置均基于此原型克隆并修改,避免重复解析配置文件。

二、原型模式的UML表示

以下是原型模式的标准 UML 类图:

#mermaid-svg-XtRTaXdaP1cFLcCE {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XtRTaXdaP1cFLcCE .error-icon{fill:#552222;}#mermaid-svg-XtRTaXdaP1cFLcCE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XtRTaXdaP1cFLcCE .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-XtRTaXdaP1cFLcCE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XtRTaXdaP1cFLcCE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XtRTaXdaP1cFLcCE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XtRTaXdaP1cFLcCE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XtRTaXdaP1cFLcCE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XtRTaXdaP1cFLcCE .marker.cross{stroke:#333333;}#mermaid-svg-XtRTaXdaP1cFLcCE svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XtRTaXdaP1cFLcCE g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-XtRTaXdaP1cFLcCE g.classGroup text .title{font-weight:bolder;}#mermaid-svg-XtRTaXdaP1cFLcCE .nodeLabel,#mermaid-svg-XtRTaXdaP1cFLcCE .edgeLabel{color:#131300;}#mermaid-svg-XtRTaXdaP1cFLcCE .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-XtRTaXdaP1cFLcCE .label text{fill:#131300;}#mermaid-svg-XtRTaXdaP1cFLcCE .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-XtRTaXdaP1cFLcCE .classTitle{font-weight:bolder;}#mermaid-svg-XtRTaXdaP1cFLcCE .node rect,#mermaid-svg-XtRTaXdaP1cFLcCE .node circle,#mermaid-svg-XtRTaXdaP1cFLcCE .node ellipse,#mermaid-svg-XtRTaXdaP1cFLcCE .node polygon,#mermaid-svg-XtRTaXdaP1cFLcCE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XtRTaXdaP1cFLcCE .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-XtRTaXdaP1cFLcCE g.clickable{cursor:pointer;}#mermaid-svg-XtRTaXdaP1cFLcCE g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-XtRTaXdaP1cFLcCE g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-XtRTaXdaP1cFLcCE .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-XtRTaXdaP1cFLcCE .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-XtRTaXdaP1cFLcCE .dashed-line{stroke-dasharray:3;}#mermaid-svg-XtRTaXdaP1cFLcCE #compositionStart,#mermaid-svg-XtRTaXdaP1cFLcCE .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE #compositionEnd,#mermaid-svg-XtRTaXdaP1cFLcCE .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE #dependencyStart,#mermaid-svg-XtRTaXdaP1cFLcCE .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE #dependencyStart,#mermaid-svg-XtRTaXdaP1cFLcCE .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE #extensionStart,#mermaid-svg-XtRTaXdaP1cFLcCE .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE #extensionEnd,#mermaid-svg-XtRTaXdaP1cFLcCE .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE #aggregationStart,#mermaid-svg-XtRTaXdaP1cFLcCE .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE #aggregationEnd,#mermaid-svg-XtRTaXdaP1cFLcCE .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XtRTaXdaP1cFLcCE .edgeTerminals{font-size:11px;}#mermaid-svg-XtRTaXdaP1cFLcCE :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}implementsimplementscontains«interface»Prototype+clone()ConcretePrototypeA-field1: String-field2: int-reference: Component+clone()ConcretePrototypeB-data: List+clone()Component-name: String

图解说明

  • Prototype 接口定义 clone() 方法,返回一个 Prototype 类型的对象。
  • ConcretePrototypeAConcretePrototypeB 是具体实现类,各自实现 clone() 方法。
  • ConcretePrototypeA 包含一个 Component 类型的引用字段,克隆时需决定是浅复制还是深复制该引用。
  • 客户端通过调用 prototype.clone() 获取新对象,无需知道其具体类名,实现了创建过程的解耦。

三、一个简单的Java程序实例

以下是一个基于原型模式的 Java 示例,模拟配置对象的克隆过程:

import java.util.ArrayList;import java.util.List;// 组件类:被引用的对象class ServerConfig { private String host; private int port; public ServerConfig(String host, int port) { this.host = host; this.port = port; } // 提供复制构造函数用于深克隆 public ServerConfig copy() { return new ServerConfig(this.host, this.port); } // Getter and Setter public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } @Override public String toString() { return \"ServerConfig{\" + \"host=\'\" + host + \'\\\'\' + \", port=\" + port + \'}\'; }}// 抽象原型接口interface Configuration extends Cloneable { Configuration clone();}// 具体原型类:应用配置class AppConfiguration implements Configuration { private String appName; private int timeout; private boolean debugMode; private ServerConfig primaryServer;  // 引用类型 private List<String> allowedOrigins; // 集合类型 // 构造函数:用于创建初始原型 public AppConfiguration(String appName, int timeout, boolean debugMode, ServerConfig primaryServer, List<String> allowedOrigins) { this.appName = appName; this.timeout = timeout; this.debugMode = debugMode; this.primaryServer = primaryServer; this.allowedOrigins = new ArrayList<>(allowedOrigins); // 防止外部修改 } // 深克隆实现 @Override public Configuration clone() { try { // 先调用 Object.clone() 进行浅克隆 AppConfiguration cloned = (AppConfiguration) super.clone(); // 对引用类型字段进行深克隆 cloned.primaryServer = this.primaryServer.copy(); // 使用复制构造函数 cloned.allowedOrigins = new ArrayList<>(this.allowedOrigins); // 复制集合内容 return cloned; } catch (CloneNotSupportedException e) { throw new RuntimeException(\"Clone failed\", e); } } // Getter and Setter 方法(省略部分) public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public boolean isDebugMode() { return debugMode; } public void setDebugMode(boolean debugMode) { this.debugMode = debugMode; } public ServerConfig getPrimaryServer() { return primaryServer; } public void setPrimaryServer(ServerConfig primaryServer) { this.primaryServer = primaryServer; } public List<String> getAllowedOrigins() { return new ArrayList<>(allowedOrigins); } @Override public String toString() { return \"AppConfiguration{\" + \"appName=\'\" + appName + \'\\\'\' + \", timeout=\" + timeout + \", debugMode=\" + debugMode + \", primaryServer=\" + primaryServer + \", allowedOrigins=\" + allowedOrigins + \'}\'; }}// 客户端使用示例public class PrototypePatternDemo { public static void main(String[] args) { // 创建一个“默认配置”原型对象 ServerConfig defaultServer = new ServerConfig(\"localhost\", 8080); List<String> defaultOrigins = List.of(\"https://example.com\", \"https://api.example.com\"); AppConfiguration defaultConfig = new AppConfiguration( \"MyApp\", 30, false, defaultServer, defaultOrigins ); System.out.println(\"=== 原始原型 ===\"); System.out.println(defaultConfig); // 克隆原型并修改部分配置,用于开发环境 AppConfiguration devConfig = (AppConfiguration) defaultConfig.clone(); devConfig.setAppName(\"MyApp-Dev\"); devConfig.setTimeout(60); devConfig.setDebugMode(true); devConfig.getPrimaryServer().setHost(\"dev-server.local\"); devConfig.getAllowedOrigins().add(\"http://localhost:3000\"); System.out.println(\"\\n=== 开发环境配置(克隆后修改)===\"); System.out.println(devConfig); // 验证原始对象未被影响(深克隆效果) System.out.println(\"\\n=== 原始原型是否被修改?===\"); System.out.println(defaultConfig); // 输出显示 defaultConfig 的 primaryServer 仍为 localhost,allowedOrigins 无 localhost:3000 }}

运行说明

  • defaultConfig 作为原型对象,可能通过复杂过程(如读取配置文件、数据库查询)创建。
  • devConfig 通过 clone() 方法创建,避免重复初始化。
  • clone() 中实现了深克隆,确保 devConfig 修改 primaryServerallowedOrigins 不影响 defaultConfig
  • 客户端无需知道 AppConfiguration 的构造细节,只需调用 clone() 即可获得新实例。

四、总结

原型模式通过对象克隆机制,实现了以下关键优势:

  • 提升性能:避免重复执行昂贵的初始化逻辑,尤其适合大型或复杂对象。
  • 简化对象创建:无需了解构造参数,只需复制已有实例。
  • 支持运行时动态性:可在运行时基于用户配置或环境动态生成对象。
  • 实现对象解耦:客户端不依赖具体类,仅通过接口调用 clone()

但也存在缺点:

  • 克隆逻辑复杂:深克隆需手动处理所有引用字段,易出错。
  • 内存开销:每个克隆对象都占用独立内存,浅克隆可能引发意外共享。
  • 不适用于所有场景:若对象状态频繁变化或包含临时资源(如连接),克隆可能导致不一致。

因此,应在“创建成本”与“内存/维护成本”之间权衡使用。

架构师洞见:
原型模式是“对象复用”与“运行时灵活性”的典范。在现代架构中,其思想已融入配置中心(如 Spring Cloud Config)、对象池(如数据库连接池预热)、游戏实体生成、A/B 测试配置分发等场景。架构师应认识到:原型模式的本质是将“对象模板”与“实例化过程”分离,实现“一次构建,多次复用”。未来,随着云原生和 Serverless 架构的发展,函数冷启动问题使得“预初始化实例池 + 克隆分发”成为优化启动延迟的有效策略。此外,结合序列化(JSON/XML/Binary)实现跨进程或跨服务的原型传递,将进一步拓展其应用边界。掌握原型模式,有助于设计出高性能、低延迟、高弹性的系统,是应对高并发与动态配置挑战的重要工具。