原型模式(Prototype Pattern)
原型模式是创建型设计模式的重要成员,其核心目标是通过复制(克隆)现有对象(原型)来创建新对象,而非通过构造函数重新初始化。它如同现实中的 “复印文件”—— 当需要多份相同格式的文档时,直接复印(克隆)原件(原型)比重新排版(重新初始化)更高效,尤其当文档格式复杂时。
一、原型模式核心概念(通用)
1. 定义
用原型实例指定创建对象的种类,并且通过复制这个原型来创建新的对象。原型模式通过克隆现有对象,避免了重复执行复杂的初始化逻辑,提高对象创建效率。
2. 意图
- 对象创建对象,在内存中复制一份对象,不走类的加载、实例化、初始化。使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
- 简化对象创建流程:对于初始化成本高(如需查询数据库、复杂计算、网络请求)的对象,通过克隆原型避免重复初始化。
- 动态生成对象:允许在运行时动态创建和配置对象,无需提前知道具体类(如通过克隆不同原型生成不同类型的对象)。
- 保留对象状态:克隆可保留原型对象的当前状态(如已填充的属性),新对象无需重新设置这些状态。
- 避免构造函数限制:当构造函数私有或参数复杂时,克隆是获取新对象的便捷方式。
3. 通用核心组件
原型模式的核心是 “原型对象” 和 “克隆方法”,包含 3 个核心角色:
| 角色名称 | 职责描述 |
|---|---|
| Prototype(抽象原型) | 声明克隆方法的接口或抽象类,通常包含clone()方法,定义克隆自身的规范。 |
| ConcretePrototype(具体原型) | 实现抽象原型的clone()方法,完成自身的克隆逻辑(浅克隆或深克隆),是被克隆的对象。 |
| Client(客户端) | 通过调用原型对象的clone()方法获取新对象,无需直接使用new关键字创建。 |
二、原型模式详细解析(以 “文档模板克隆” 为例)
以 “文档编辑系统” 为场景:系统中有多种文档模板(如简历模板、报告模板),每个模板包含复杂格式(字体、布局、页眉页脚)和初始内容。用户需基于模板创建新文档 —— 若每次创建都重新设置格式(调用构造函数初始化),会重复执行耗时操作;原型模式通过克隆模板(原型),直接生成包含相同格式的新文档,用户只需修改内容即可,大幅提升效率。
1. 结构
- Prototype(抽象原型):
Document,声明clone()方法和setContent()方法(修改内容)。 - ConcretePrototype(具体原型):
ResumeTemplate(简历模板)、ReportTemplate(报告模板),实现clone()方法,克隆自身的格式和初始状态。 - Client(客户端):通过调用模板的
clone()方法生成新文档,修改内容后使用。
2. 类图(Mermaid)

classDiagram
%% 抽象原型:文档接口
class Document {
<<Interface>>
+clone(): Document // 克隆方法
+setContent(content: String): void // 设置内容
+show(): void // 显示文档
}
%% 具体原型1:简历模板
class ResumeTemplate {
-format: String // 格式(如字体、布局)
-content: String // 内容
+ResumeTemplate() // 构造器:初始化复杂格式
+clone(): Document // 实现克隆
+setContent(content: String): void
+show(): void
}
%% 具体原型2:报告模板
class ReportTemplate {
-format: String // 格式
-content: String // 内容
+ReportTemplate() // 构造器:初始化复杂格式
+clone(): Document // 实现克隆
+setContent(content: String): void
+show(): void
}
%% 客户端
class Client {
+createDocument(): void
}
%% 关系梳理
%% 具体原型实现抽象原型
Document <|-- ResumeTemplate
Document <|-- ReportTemplate
Client --> Document : 调用clone()获取新对象
3. 时序图(Mermaid)
以 “客户端克隆简历模板创建新文档” 为例,展示原型模式的调用流程:
sequenceDiagram
participant Client(客户端)
participant ResumeTemplate(具体原型:简历模板)
participant Document(抽象原型)
participant NewDocument(克隆的新文档)
%% 1. 客户端获取原型对象(简历模板)
Client->>ResumeTemplate: new ResumeTemplate()(初始化模板,设置复杂格式)
ResumeTemplate-->>Client: 返回原型实例(包含初始格式和内容)
%% 2. 客户端调用克隆方法
Client->>Document: clone()(多态调用ResumeTemplate的clone)
ResumeTemplate->>NewDocument: 复制自身属性(格式、内容)
NewDocument-->>ResumeTemplate: 返回克隆对象
ResumeTemplate-->>Document: 克隆对象向上转型为Document
Document-->>Client: 客户端获取新文档
%% 3. 客户端修改新文档内容(不影响原型)
Client->>NewDocument: setContent("张三的简历内容...")
NewDocument-->>Client: 内容修改完成
%% 4. 显示新文档
Client->>NewDocument: show()
NewDocument-->>Client: 显示:"格式:简历模板;内容:张三的简历内容..."
4. 优点
- 提高创建效率:克隆避免了重复执行原型对象的复杂初始化逻辑(如数据库查询、网络请求),尤其适合创建成本高的对象。
- 简化创建过程:客户端无需了解对象的创建细节(如构造函数参数、初始化步骤),只需调用
clone()即可。 - 动态扩展对象:可在运行时动态添加新的原型(如通过配置文件加载新模板),客户端无需修改代码即可克隆新对象。
- 保留对象状态:克隆生成的新对象继承原型的当前状态(如已设置的属性),无需重新配置。
5. 缺点
- 深克隆实现复杂:当对象包含引用类型成员(如集合、自定义对象)时,浅克隆会导致新对象与原型共享引用(修改一个会影响另一个),深克隆需递归复制所有引用对象,实现复杂。
- 需为每个类实现克隆方法:所有具体原型都需重写
clone(),增加代码量;若类结构修改(如新增属性),需同步更新克隆方法,否则可能遗漏新属性。 - 隐藏对象创建逻辑:克隆方法内部的复制逻辑可能复杂,若实现不当(如忘记复制某个属性),难以排查问题。
三、Java 代码实现(文档模板克隆示例)
1. 抽象原型(Document)
定义克隆方法和文档操作接口:
// 抽象原型:文档接口
public interface Document extends Cloneable {
// 克隆方法
Document clone();
// 设置文档内容
void setContent(String content);
// 显示文档
void show();
}
2. 具体原型(ResumeTemplate、ReportTemplate)
实现克隆方法,注意浅克隆与深克隆的区别:
(1)浅克隆示例(属性均为基本类型或不可变对象)
// 具体原型1:简历模板(浅克隆适用)
public class ResumeTemplate implements Document {
private String format; // 格式(基本类型引用,不可变对象)
private String content; // 内容
// 构造器:初始化复杂格式(模拟高成本操作)
public ResumeTemplate() {
System.out.println("初始化简历模板格式(耗时操作:设置字体、布局、页眉...)");
this.format = "简历格式:标题居中,正文宋体五号,包含教育经历、工作经验栏";
this.content = "【个人简历模板】\n姓名:\n年龄:\n...";
}
// 浅克隆:直接复制属性(因format是不可变String,content是String,无需深克隆)
@Override
public Document clone() {
try {
return (Document) super.clone(); // 调用Object的clone()(浅克隆)
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
@Override
public void setContent(String content) {
this.content = content;
}
@Override
public void show() {
System.out.println("文档类型:简历\n" + format + "\n内容:" + content);
}
}
// 具体原型2:报告模板(浅克隆适用)
public class ReportTemplate implements Document {
private String format;
private String content;
public ReportTemplate() {
System.out.println("初始化报告模板格式(耗时操作:设置章节、页眉页脚、页码...)");
this.format = "报告格式:一级标题黑体三号,二级标题宋体四号,包含摘要、正文、结论";
this.content = "【报告模板】\n摘要:\n正文:\n...";
}
@Override
public Document clone() {
try {
return (Document) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
@Override
public void setContent(String content) {
this.content = content;
}
@Override
public void show() {
System.out.println("文档类型:报告\n" + format + "\n内容:" + content);
}
}
(2)深克隆示例(包含引用类型成员)
当原型包含引用类型(如List)时,需实现深克隆:
import java.util.ArrayList;
import java.util.List;
// 包含引用类型的文档模板(需深克隆)
public class ComplexDocument implements Document {
private String format;
private List<String> sections; // 引用类型:章节列表
public ComplexDocument() {
System.out.println("初始化复杂文档模板(耗时操作)");
this.format = "复杂文档格式";
this.sections = new ArrayList<>();
sections.add("前言");
sections.add("正文");
sections.add("后记"); // 初始化章节
}
// 深克隆:不仅复制自身,还需复制引用类型成员
@Override
public Document clone() {
try {
ComplexDocument clone = (ComplexDocument) super.clone(); // 浅克隆基础属性
// 深克隆引用类型:创建新List并复制元素
clone.sections = new ArrayList<>(this.sections);
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
@Override
public void setContent(String content) {
// 简化示例:向章节添加内容
if (!sections.isEmpty()) {
sections.set(1, "正文:" + content);
}
}
@Override
public void show() {
System.out.println("文档类型:复杂文档\n" + format + "\n章节:" + sections);
}
}
2.1 其他深拷贝方法
1. 序列化(Serialization)
通过将对象写入流再读取,利用 Java 的序列化机制实现深拷贝:
import java.io.*;
public class SerializationDeepCopy {
public static <T extends Serializable> T deepCopy(T obj) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis)) {
return (T) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
**2. Android Parcelable **
看后面...Parcelable 与对象复制
3. 第三方库(如 Apache Commons Lang)
使用 SerializationUtils.clone() 方法:
import org.apache.commons.lang3.SerializationUtils;
Person deepCopy = (Person) SerializationUtils.clone(originalPerson);
3. 客户端(Client)
通过克隆原型创建新对象:
// 客户端:使用原型模式创建文档
public class Client {
public static void main(String[] args) {
System.out.println("=== 克隆简历模板 ===");
Document resumeTemplate = new ResumeTemplate(); // 创建原型(初始化成本高)
Document resume1 = resumeTemplate.clone(); // 克隆1
resume1.setContent("张三的个人简历:2020年毕业于XX大学...");
resume1.show();
Document resume2 = resumeTemplate.clone(); // 克隆2
resume2.setContent("李四的个人简历:2018年入职XX公司...");
resume2.show();
System.out.println("\n=== 克隆复杂文档(深克隆测试) ===");
ComplexDocument complexTemplate = new ComplexDocument();
ComplexDocument complex1 = (ComplexDocument) complexTemplate.clone();
complex1.setContent("深克隆测试内容");
complex1.show(); // 章节包含修改后的内容
// 验证深克隆:修改克隆对象的引用成员,不影响原型
complex1.sections.add("附录");
System.out.println("原型的章节:" + complexTemplate.sections); // 无"附录"
System.out.println("克隆对象的章节:" + complex1.sections); // 有"附录"
}
}
4. 输出结果
=== 克隆简历模板 ===
初始化简历模板格式(耗时操作:设置字体、布局、页眉...)
文档类型:简历
简历格式:标题居中,正文宋体五号,包含教育经历、工作经验栏
内容:张三的个人简历:2020年毕业于XX大学...
文档类型:简历
简历格式:标题居中,正文宋体五号,包含教育经历、工作经验栏
内容:李四的个人简历:2018年入职XX公司...
=== 克隆复杂文档(深克隆测试) ===
初始化复杂文档模板(耗时操作)
文档类型:复杂文档
复杂文档格式
章节:[前言, 正文:深克隆测试内容, 后记]
原型的章节:[前言, 正文, 后记]
克隆对象的章节:[前言, 正文:深克隆测试内容, 后记, 附录]
四、适用环境
原型模式适用于以下场景,核心判断标准是 “对象创建成本高,或需保留原型状态快速生成新对象”:
- 对象初始化成本高:对象创建需执行复杂计算、数据库查询、网络请求等耗时操作(如文档模板、数据库连接池中的连接对象)。
- 需动态生成多个相似对象:多个对象结构相同但内容略有差异(如批量生成格式相同的报表,仅数据不同)。
- 需保留对象的中间状态:创建对象后需经过多步配置才能使用,克隆可直接复用这些配置(如已设置好参数的绘图工具)。
- 避免构造函数限制:构造函数私有(无法直接
new)、参数复杂难以获取,或构造函数逻辑不适合重复执行。 - 需在运行时动态添加对象类型:系统需支持未知类型的对象(如插件系统,通过克隆已加载的插件原型生成新实例)。
五、模式分析
1. 核心本质
原型模式的本质是 “通过复制实现对象复用”:
- 复制而非创建:利用现有对象的状态复制出新对象,避免重复初始化;
- 原型即模板:原型对象作为 “模板”,定义了新对象的初始状态和结构;
- 克隆接口统一:通过
clone()方法统一克隆逻辑,客户端无需区分具体类型。
2. 浅克隆 vs 深克隆(核心区别)
| 类型 | 定义 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 浅克隆(Shallow Clone) | 仅复制对象本身及基本类型成员,引用类型成员仅复制引用地址(新对象与原型共享引用对象)。 | 实现简单(直接调用Object.clone()),性能好。 | 引用类型成员修改会影响原型(或被原型影响),存在副作用。 | 对象仅包含基本类型或不可变引用类型(如String)。 |
| 深克隆(Deep Clone) | 不仅复制对象本身,还递归复制所有引用类型成员(新对象与原型的引用对象完全独立)。 | 新对象与原型完全隔离,修改互不影响。 | 实现复杂(需手动复制所有引用对象),性能开销大。 | 对象包含可变引用类型(如List、自定义对象)。 |
| 维度 | 浅拷贝 | 深拷贝 |
|---|---|---|
| 内存结构 | 共享引用类型的内存地址 | 完全独立的内存地址 |
| 实现方式 | 重写 clone(),直接调用 super.clone() | 递归克隆所有引用类型属性 |
| 修改影响 | 修改引用类型属性会影响原对象 | 修改不影响原对象 |
| 性能开销 | 低(仅复制引用) | 高(递归复制所有对象) |
| 适用场景 | 对象简单,无需独立修改引用类型属性 | 对象复杂,需要完全独立的副本 |
3. 与其他模式的区别
| 模式 | 核心差异 | 典型场景 |
|---|---|---|
| 原型模式 | 通过克隆现有对象创建新对象,强调 “复制复用” | 文档模板、高成本对象创建 |
| 工厂方法模式 | 通过子类决定创建哪种对象,强调 “延迟创建” | 汽车生产、多产品类型 |
| 抽象工厂模式 | 创建产品族(相关对象集合),强调 “族内兼容” | 家电品牌、UI 主题 |
| 建造者模式 | 分步构建复杂对象,强调 “创建过程” | 电脑组装、复杂文档生成 |
4. 关键设计原则
- 单一职责原则:克隆方法应仅负责对象复制,避免在
clone()中添加与复制无关的逻辑(如业务处理)。 - 明确克隆类型:根据对象成员类型(基本类型 / 引用类型)选择浅克隆或深克隆,避免隐含的共享引用问题。
- 简化原型初始化:原型对象的初始化应集中在构造函数或专门的
init()方法中,确保克隆对象继承正确的初始状态。
六、模式扩展
原型模式可通过以下方式扩展,增强灵活性和实用性:
1. 原型管理器(Prototype Manager)
当系统中存在多个原型时,通过管理器集中存储和管理原型,客户端通过名称或 ID 获取原型并克隆,简化原型的复用和管理:
import java.util.HashMap;
import java.util.Map;
// 原型管理器:管理多个文档模板
public class DocumentManager {
// 存储原型:key为模板名称,value为原型对象
private static final Map<String, Document> prototypes = new HashMap<>();
// 注册原型
public static void registerPrototype(String name, Document prototype) {
prototypes.put(name, prototype);
}
// 获取并克隆原型
public static Document getPrototype(String name) {
Document prototype = prototypes.get(name);
if (prototype == null) {
throw new IllegalArgumentException("未知模板:" + name);
}
return prototype.clone(); // 克隆后返回
}
}
// 客户端使用原型管理器
public class Client {
public static void main(String[] args) {
// 初始化管理器(通常在系统启动时执行)
DocumentManager.registerPrototype("resume", new ResumeTemplate());
DocumentManager.registerPrototype("report", new ReportTemplate());
// 无需直接创建原型,通过管理器克隆
Document resume = DocumentManager.getPrototype("resume");
Document report = DocumentManager.getPrototype("report");
}
}
2. 带工厂的原型模式
结合工厂模式,通过工厂封装克隆逻辑,客户端通过工厂获取新对象,进一步隐藏克隆细节:
// 原型工厂
public class DocumentFactory {
private Document prototype;
// 注入原型
public DocumentFactory(Document prototype) {
this.prototype = prototype;
}
// 工厂方法:通过克隆创建新对象
public Document createDocument() {
return prototype.clone();
}
}
// 客户端使用工厂
public class Client {
public static void main(String[] args) {
DocumentFactory resumeFactory = new DocumentFactory(new ResumeTemplate());
Document resume1 = resumeFactory.createDocument(); // 工厂内部调用克隆
Document resume2 = resumeFactory.createDocument();
}
}
3. 序列化实现深克隆
对于复杂对象(多层嵌套引用类型),可通过序列化(将对象转为字节流)和反序列化(重建对象)实现深克隆,避免手动递归复制:
import java.io.*;
// 支持序列化的深克隆工具类
public class DeepCloneUtils {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T obj) {
try {
// 序列化:将对象写入字节流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
// 反序列化:从字节流重建对象(新对象,与原对象完全独立)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
} catch (Exception e) {
throw new RuntimeException("深克隆失败", e);
}
}
}
// 示例:使用序列化深克隆
public class SerializableDocument implements Document, Serializable {
private String format;
private List<String> sections = new ArrayList<>(); // 引用类型
@Override
public Document clone() {
return DeepCloneUtils.deepClone(this); // 用工具类实现深克隆
}
// 其他方法...
}
七、模式应用(通用 + Android)
1. 通用领域应用
Java 集合框架:
ArrayList.clone() 、HashMap.clone() 等方法实现浅克隆,快速复制集合结构(元素为引用类型时需注意共享问题)。List<String> list = new ArrayList<>(); list.add("a"); List<String> cloneList = (List<String>) ((ArrayList<String>) list).clone(); // 浅克隆Spring 框架:
BeanDefinition的克隆用于创建相似的 Bean 定义;Prototype作用域的 Bean 通过克隆(或重新实例化)确保每次获取新对象。设计工具(如 Photoshop):图层复制功能通过克隆现有图层(原型)创建新图层,保留原图层的样式和属性。
数据库连接池:连接池中的连接对象(
Connection)通过克隆(或重置状态)复用,避免频繁创建和关闭连接的开销。
2. Android 中的应用
Android 框架和开发中,原型模式常用于高效创建复杂对象或复用现有对象状态:
(1)Intent 的克隆(浅克隆)
场景:
Intent用于组件间通信,包含 Action、Data、Extra 等数据。当需要传递相似的Intent(如仅修改 Extra)时,克隆比重新创建更高效。实现:
Intent的clone()方法实现浅克隆,复制所有属性(mAction、mData等),但mExtras(Bundle)为引用类型,需注意共享问题。// 克隆Intent Intent originalIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com")); Intent clonedIntent = (Intent) originalIntent.clone(); // 浅克隆 clonedIntent.putExtra("key", "value"); // 修改克隆对象的Extra,不影响原对象(因Bundle是不可变的?不,实际需注意)
(2)Bitmap 的复制(深克隆)
场景:
Bitmap是内存密集型对象(创建成本高),复制Bitmap时需通过copy()方法(深克隆)生成独立实例,避免共享像素数据。实现:
Bitmap.copy(Bitmap.Config config, boolean isMutable)创建新Bitmap并复制像素数据,与原对象完全独立。// 深克隆Bitmap Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image); Bitmap clonedBitmap = originalBitmap.copy(originalBitmap.getConfig(), true); // 深克隆 originalBitmap.recycle(); // 释放原对象,不影响克隆对象
(3)Parcelable 与对象复制
场景:
Parcelable是 Android 中高效的序列化接口,常用于跨进程传递对象。通过Parcelable的writeToParcel()和CREATOR,可实现对象的深克隆(序列化 + 反序列化)。实现:
public class User implements Parcelable { private String name; private int age; // 克隆方法:通过Parcelable实现深克隆 public User clone() { Parcel parcel = Parcel.obtain(); writeToParcel(parcel, 0); parcel.setDataPosition(0); // 重置指针 User clone = CREATOR.createFromParcel(parcel); parcel.recycle(); return clone; } // Parcelable其他方法... public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() { ... }; }
(4)View 的复制(原型模式思想)
- 场景:动态生成多个相同结构的
View(如列表项)时,通过克隆原型View避免重复inflate(布局加载成本高)。 - 实现:缓存
inflate后的原型View,通过clone()或手动复制属性生成新View。
八、总结
原型模式是 “高效对象创建” 的核心解决方案,其核心价值在于 “复制复用现有对象,避免重复初始化”:
- 核心优势:大幅降低高成本对象的创建开销;简化相似对象的生成过程;支持动态扩展和状态保留;符合 “开闭原则”(新增原型无需修改客户端)。
- 适用场景:对象初始化成本高、需生成多个相似对象、需保留中间状态、构造函数受限的场景(如文档模板、
Bitmap复制、Intent复用)。 - 实践建议:
- 优先使用浅克隆(简单高效),仅在包含可变引用类型时使用深克隆;
- 深克隆优先通过序列化实现(复杂对象),或手动复制引用成员(简单对象);
- 原型较多时使用原型管理器,集中管理和复用原型;
- 克隆方法中仅保留复制逻辑,避免添加业务处理,确保单一职责。
原型模式不仅是一种创建对象的技巧,更是一种 “复用思维” 的体现 —— 通过复制已有成果(原型)快速生成新成果,在不牺牲灵活性的前提下提升效率,是处理高成本对象创建场景的理想选择。