原型模式
原型模式:高效创建对象的设计模式详解
在软件开发中,当创建一个对象的成本较高(如需要复杂初始化、频繁访问数据库或网络)时,直接通过new关键字重复创建同类对象会浪费大量资源。原型模式(Prototype Pattern)正是为解决这一问题而生 —— 它通过 “复制已有实例” 的方式创建新对象,大幅降低创建成本,同时保证对象属性的一致性。下文将从定义、原理、实现到应用,全面拆解原型模式。
一、原型模式的定义与核心意图
1. 定义
原型模式(Prototype Pattern)是创建型设计模式的一种,其核心思想是:用一个已存在的实例(原型)作为模板,通过复制(克隆)该实例的方式创建新的对象,而无需重新执行复杂的初始化流程。
2. 核心意图
-
降低对象创建成本:避免重复执行耗时、耗资源的初始化操作(如读取配置文件、建立数据库连接、解析大型文件);
-
保证对象属性一致性:新对象复制原型的属性,确保初始状态与原型一致,避免手动赋值导致的错误;
-
灵活扩展对象类型:无需修改现有代码,只需新增原型类即可扩展对象类型,符合 “开闭原则”。
3. 生活类比
原型模式类似 “复印文件”:当你有一份已排版好的文档(原型),需要多份相同文档时,直接复印(克隆)比重新打字排版(new创建)更高效,且能保证每份文档内容完全一致。
二、原型模式的工作原理与 UML 类图
1. 核心原理
原型模式的实现依赖两个关键操作:
-
定义原型接口:声明一个
clone()方法,用于约定 “克隆自身” 的行为; -
实现克隆逻辑:具体原型类实现
clone()方法,通过浅克隆或深克隆复制自身属性,返回新对象; -
使用原型管理器(可选):当原型实例较多时,用一个 “管理器” 存储和管理原型,按需获取并克隆,降低代码耦合。
2. 关键角色
| 角色名称 | 职责描述 |
|---|---|
| 抽象原型(Prototype) | 通常是接口或抽象类,声明clone()方法,定义克隆的统一规范; |
| 具体原型(Concrete Prototype) | 实现抽象原型的clone()方法,完成自身属性的复制,是被克隆的具体实例; |
| 原型管理器(Prototype Manager) | 可选角色,存储多个原型实例,提供 “获取原型”“新增原型” 的方法,简化克隆调用; |
3. UML 类图
三、原型模式的两种实现:浅克隆与深克隆
原型模式的核心是 “克隆”,但根据对象属性的类型(基本数据类型 / 引用数据类型),克隆分为浅克隆和深克隆,二者的区别在于是否复制引用类型的属性。
1. 浅克隆(Shallow Clone)
核心特点
-
只复制对象的 “基本数据类型属性”(如
int、String、long)和 “引用数据类型属性的引用地址”,不复制引用指向的实际对象; -
新对象与原型对象的引用类型属性共享同一个实例,修改其中一个会影响另一个。
实现方式
在 Java 中,浅克隆可通过实现Cloneable接口(标记接口,无实际方法),并重写Object类的clone()方法实现(Object.clone()默认是浅克隆)。
代码示例:浅克隆实现
import java.util.ArrayList;
import java.util.List;
// 1. 抽象原型(此处用接口模拟,也可直接用具体类实现Cloneable)
interface Prototype extends Cloneable {
Prototype clone();
}
// 2. 具体原型类:包含基本类型和引用类型属性
class Product implements Prototype {
// 基本数据类型属性
private String name;
private double price;
// 引用数据类型属性(List)
private List\<String> tags;
// 构造方法:模拟复杂初始化(如从数据库加载数据)
public Product(String name, double price, List\<String> tags) {
System.out.println("执行复杂初始化操作(耗时100ms)...");
this.name = name;
this.price = price;
this.tags = tags;
// 模拟耗时操作:如读取配置、建立连接
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 3. 实现克隆方法(浅克隆)
@Override
public Product clone() {
try {
// 调用Object.clone()完成浅克隆,无需重新执行构造方法
return (Product) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败:" + e.getMessage());
}
}
// Getter/Setter:用于测试属性修改
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List\<String> getTags() { return tags; }
@Override
public String toString() {
return "Product{name='" + name + "', price=" + price + ", tags=" + tags + "}";
}
}
// 测试浅克隆
public class ShallowCloneTest {
public static void main(String\[] args) {
// 1. 创建原型实例(执行复杂初始化)
List\<String> prototypeTags = new ArrayList<>();
prototypeTags.add("电子设备");
prototypeTags.add("新品");
Product prototype = new Product("手机", 3999.99, prototypeTags);
System.out.println("原型实例:" + prototype);
// 2. 克隆新对象(无需执行构造方法,耗时极短)
Product clone1 = prototype.clone();
Product clone2 = prototype.clone();
System.out.println("\n克隆对象1:" + clone1);
System.out.println("克隆对象2:" + clone2);
// 3. 测试引用类型属性共享问题
clone1.getTags().add("促销"); // 修改clone1的tags
System.out.println("\n修改clone1的tags后:");
System.out.println("原型实例tags:" + prototype.getTags()); // 原型的tags也被修改
System.out.println("克隆对象1tags:" + clone1.getTags());
System.out.println("克隆对象2tags:" + clone2.getTags()); // clone2的tags也被修改
}
}
输出结果与分析
执行复杂初始化操作(耗时100ms)...
原型实例:Product{name='手机', price=3999.99, tags=\[电子设备, 新品]}
克隆对象1:Product{name='手机', price=3999.99, tags=\[电子设备, 新品]}
克隆对象2:Product{name='手机', price=3999.99, tags=\[电子设备, 新品]}
修改clone1的tags后:
原型实例tags:\[电子设备, 新品, 促销] // 原型被影响
克隆对象1tags:\[电子设备, 新品, 促销]
克隆对象2tags:\[电子设备, 新品, 促销] // clone2被影响
结论:浅克隆高效(无需重复初始化),但引用类型属性共享,修改会相互影响,适用于 “引用类型属性无需修改” 的场景。
2. 深克隆(Deep Clone)
核心特点
-
不仅复制基本数据类型属性,还会递归复制引用类型属性指向的实际对象;
-
新对象与原型对象的引用类型属性完全独立,修改其中一个不会影响另一个,是更安全的克隆方式。
实现方式
Java 中深克隆有两种常用实现:
-
方式 1:在
clone()方法中手动复制引用类型属性(适合引用类型较少的场景); -
方式 2:通过序列化(
Serializable)将对象转化为字节流,再反序列化为新对象(适合引用类型较多或嵌套较深的场景)。
代码示例 1:手动复制实现深克隆
基于上文Product类,修改clone()方法,手动复制tags列表:
@Override
public Product clone() {
try {
// 1. 先执行浅克隆,获取基本类型属性的复制
Product clone = (Product) super.clone();
// 2. 手动复制引用类型属性(创建新的List,避免共享)
List\<String> newTags = new ArrayList<>(this.tags); // 复制原tags的元素
clone.tags = newTags;
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败:" + e.getMessage());
}
}
代码示例 2:序列化实现深克隆
当引用类型嵌套较深(如List中包含Map,Map中包含自定义对象)时,手动复制繁琐,可通过序列化实现通用深克隆:
import java.io.\*;
import java.util.ArrayList;
import java.util.List;
// 1. 实现Serializable接口(支持序列化)
class ProductDeepClone implements Prototype, Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号(避免版本冲突)
private String name;
private double price;
private List\<String> tags; // 引用类型属性
// 复杂构造方法
public ProductDeepClone(String name, double price, List\<String> tags) {
System.out.println("执行复杂初始化操作(耗时100ms)...");
this.name = name;
this.price = price;
this.tags = tags;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 2. 实现克隆方法(基于序列化的深克隆)
@Override
public ProductDeepClone clone() {
try {
// 步骤1:将原型对象序列化为字节流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 步骤2:将字节流反序列化为新对象(深克隆)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ProductDeepClone) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("深克隆失败:" + e.getMessage());
}
}
// Getter/Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List\<String> getTags() { return tags; }
@Override
public String toString() {
return "ProductDeepClone{name='" + name + "', price=" + price + ", tags=" + tags + "}";
}
}
// 测试深克隆
public class DeepCloneTest {
public static void main(String\[] args) {
// 1. 创建原型实例
List\<String> prototypeTags = new ArrayList<>();
prototypeTags.add("电子设备");
prototypeTags.add("新品");
ProductDeepClone prototype = new ProductDeepClone("手机", 3999.99, prototypeTags);
System.out.println("原型实例:" + prototype);
// 2. 深克隆新对象
ProductDeepClone clone = prototype.clone();
System.out.println("\n深克隆对象:" + clone);
// 3. 测试引用类型属性独立性
clone.getTags().add("促销"); // 修改克隆对象的tags
System.out.println("\n修改克隆对象的tags后:");
System.out.println("原型实例tags:" + prototype.getTags()); // 原型不受影响
System.out.println("克隆对象tags:" + clone.getTags()); // 仅克隆对象变化
}
}
输出结果与分析
执行复杂初始化操作(耗时100ms)...
原型实例:ProductDeepClone{name='手机', price=3999.99, tags=\[电子设备, 新品]}
深克隆对象:ProductDeepClone{name='手机', price=3999.99, tags=\[电子设备, 新品]}
修改克隆对象的tags后:
原型实例tags:\[电子设备, 新品] // 原型不受影响
克隆对象tags:\[电子设备, 新品, 促销] // 仅克隆对象变化
结论:深克隆解决了引用类型属性共享的问题,安全性更高,但实现成本略高(序列化 / 反序列化有一定性能开销),适用于 “引用类型属性需要独立修改” 的场景。
四、原型管理器(Prototype Manager)的实现与应用
当系统中存在多个原型实例(如不同类型的产品、不同风格的文档)时,直接手动管理原型会导致代码冗余。原型管理器可集中存储和管理原型,按需获取并克隆,简化调用流程。
代码示例:原型管理器
import java.util.HashMap;
import java.util.Map;
// 原型管理器:管理多个原型实例
class ProductPrototypeManager {
// 存储原型:key为原型标识(如产品类型),value为原型实例
private Map\<String, Prototype> prototypeMap = new HashMap<>();
// 1. 注册原型
public void registerPrototype(String key, Prototype prototype) {
if (key == null || prototype == null) {
throw new IllegalArgumentException("key和prototype不能为空");
}
prototypeMap.put(key, prototype);
}
// 2. 根据key获取原型并克隆
public Prototype clonePrototype(String key) {
Prototype prototype = prototypeMap.get(key);
if (prototype == null) {
throw new RuntimeException("未找到key为" + key + "的原型");
}
// 克隆并返回新对象
return prototype.clone();
}
}
// 测试原型管理器
public class PrototypeManagerTest {
public static void main(String\[] args) {
// 1. 创建原型实例
List\<String> phoneTags = new ArrayList<>();
phoneTags.add("电子设备");
Product phonePrototype = new Product("手机", 3999.99, phoneTags);
List\<String> laptopTags = new ArrayList<>();
laptopTags.add("电子设备");
laptopTags.add("办公");
Product laptopPrototype = new Product("笔记本电脑", 5999.99, laptopTags);
// 2. 初始化原型管理器并注册原型
ProductPrototypeManager manager = new ProductPrototypeManager();
manager.registerPrototype("phone", phonePrototype);
manager.registerPrototype("laptop", laptopPrototype);
// 3. 从管理器获取原型并克隆
Product phoneClone = (Product) manager.clonePrototype("phone");
Product laptopClone = (Product) manager.clonePrototype("laptop");
// 修改克隆对象的属性(浅克隆注意引用类型)
phoneClone.setName("高端手机");
laptopClone.setName("轻薄笔记本");
System.out.println("手机克隆对象:" + phoneClone);
System.out.println("笔记本克隆对象:" + laptopClone);
}
}
输出结果
执行复杂初始化操作(耗时100ms)...
执行复杂初始化操作(耗时100ms)...
手机克隆对象:Product{name='高端手机', price=3999.99, tags=\[电子设备, 新品]}
笔记本克隆对象:Product{name='轻薄笔记本', price=5999.99, tags=\[电子设备, 办公]}
优势:原型管理器将 “原型注册” 与 “克隆调用” 分离,新增原型时只需调用registerPrototype(),无需修改克隆逻辑,符合 “开闭原则”,适合多原型场景(如电商系统的商品模板、文档系统的模板管理)。
五、原型模式的适用场景
原型模式的核心价值是 “高效克隆”,以下场景尤其适合使用:
1. 对象创建成本高的场景
-
初始化需要频繁访问数据库、网络或读取大型文件(如加载商品详情、解析 Excel 模板);
-
构造方法复杂,包含大量属性赋值或业务逻辑(如创建复杂报表对象、配置对象)。
-
示例:电商系统中,创建 “商品详情页对象” 需要从数据库加载商品信息、从缓存加载库存、从 CDN 加载图片链接,用原型模式克隆可避免重复执行这些操作。
2. 需要批量创建相似对象的场景
-
需创建多个属性基本一致、仅少数属性不同的对象(如批量生成订单、批量创建测试数据);
-
示例:团购系统中,同一批次的订单除 “用户 ID”“订单编号” 外,“商品 ID”“价格”“优惠规则” 均相同,用原型模式克隆原型订单后修改差异属性,比每次
new更高效。
3. 对象类型动态扩展的场景
-
系统需支持动态新增对象类型,且无需修改现有创建逻辑(如插件系统、模板系统);
-
示例:文档编辑软件中,用户可自定义 “简历模板”“报告模板”,将这些模板作为原型注册到管理器,用户使用时直接克隆并修改内容,无需开发新的模板创建逻辑。
4. 避免构造方法暴露的场景
-
不希望通过构造方法暴露对象的初始化细节(如涉及敏感配置、复杂依赖),通过克隆隐藏创建逻辑;
-
示例:安全框架中,“加密对象” 的初始化需要加载密钥、设置加密算法,这些细节不宜暴露,可通过原型克隆提供对象,隐藏构造逻辑。
六、原型模式的优缺点
优点
-
降低创建成本:克隆无需重复执行复杂初始化,大幅提升对象创建效率;
-
简化创建逻辑:无需手动编写多个构造方法或工厂类,通过克隆即可生成新对象;
-
灵活扩展:新增原型类无需修改现有代码,符合 “开闭原则”;
-
保证属性一致:新对象与原型属性初始一致,避免手动赋值导致的错误。
缺点
-
克隆逻辑复杂:当对象包含多层嵌套的引用类型属性时,深克隆的实现(手动复制或序列化)较为繁琐;
-
序列化性能开销:基于序列化的深克隆有一定性能损耗,不适合对性能要求极高的场景;
-
依赖特定接口:Java 中需实现
Cloneable或Serializable接口,增加了对框架的依赖; -
构造方法不执行:克隆过程不调用构造方法,若构造方法中包含必要的初始化逻辑(如注册监听器),需手动在
clone()方法中补充,易遗漏。
七、原型模式与其他设计模式的对比
1. 原型模式 vs 工厂模式
| 对比维度 | 原型模式 | 工厂模式(简单工厂 / 工厂方法) |
|---|---|---|
| 核心逻辑 | 复制已有实例创建新对象 | 通过工厂类的方法创建新对象(new) |
| 适用场景 | 对象创建成本高、需批量创建相似对象 | 对象创建逻辑复杂、需统一管理创建流程 |
| 性能 | 克隆效率高(无重复初始化) | 创建效率低(需重复执行构造 / 初始化) |
| 灵活性 | 新增原型无需修改管理器,扩展灵活 | 新增产品需修改工厂(简单工厂)或新增工厂(工厂方法) |
2. 原型模式 vs 单例模式
| 对比维度 | 原型模式 | 单例模式 |
|---|---|---|
| 核心目标 | 高效创建多个相似对象 | 确保全局只有一个对象 |
| 对象数量 | 允许创建多个对象(克隆产生) | 仅允许创建一个对象 |
| 适用场景 | 批量创建对象、降低创建成本 | 资源共享、全局状态管理 |
| 实现关键 | 实现clone()方法,支持复制 |
私有化构造方法,提供全局访问点 |
八、总结
原型模式是一种 “以复制代创建” 的设计模式,核心是通过克隆已有实例(原型)生成新对象,从而降低创建成本、保证属性一致性。在实际开发中,需注意以下关键点:
-
选择克隆方式:浅克隆适合引用类型无需修改的场景,深克隆适合引用类型需独立的场景;
-
合理使用管理器:多原型场景下,用原型管理器集中管理,简化调用;
-
避免克隆陷阱:深克隆需确保所有引用类型属性都被复制,克隆过程中需补充构造方法中的必要逻辑。
当你遇到 “对象创建成本高”“需批量创建相似对象” 的场景时,原型模式会是比直接new或工厂模式更高效的选择。

浙公网安备 33010602011771号