1. 什么是原型模式
原型模式(Prototype Pattern)是 GoF 23种设计模式中对象创建型设计模式 之一,其核心思想是通过复制一个已有的对象(原型)来创建新的对象,而不是通过 new 关键字和构造函数来创建。
2.为什么需要原型模式
当对象的创建过程成本高昂 、结构复杂 或依赖于运行时动态状态时,直接通过构造函数或工厂方法频繁地从头创建实例会导致:
- 性能开销大:每次创建都需要执行耗时的操作(如数据库连接、文件读取、网络请求、复杂计算等),影响系统效率。
- 重复初始化逻辑:多个相似对象需要执行相同的初始化流程,造成资源浪费和代码冗余。
- 难以动态复制已有状态:无法便捷地基于一个已配置好的对象生成副本,尤其是在对象包含大量可变状态或深层配置的情况下。
- 对具体类的强依赖 :客户端需明确知道类名并使用
new
创建实例,导致代码耦合度高,扩展性差。
原型模式通过提供一个 clone()
方法,允许对象自行复制自身,从而避免了上述问题。它使得:
- 可以基于现有对象快速生成新实例,无需重复昂贵的构建过程;
- 支持在运行时动态决定对象的类型和结构,提升系统的灵活性;
- 客户端与具体类解耦,只需操作原型接口即可创建对象;
- 复杂对象的复用变得更加简单和安全。
因此,原型模式特别适用于需要高频创建相似对象 或对象初始化代价较高的场景,是提升性能与可维护性的重要手段。
3. 角色与UML
classDiagram
%% 定义接口
class Prototype {
<>
+clone(): Prototype
}
%% 定义具体类
class Document {
-title: String
-content: String
-author: String
+Document(title: String, content: String, author: String)
+clone(): Document
+getTitle(): String
+setTitle(title: String): void
+getContent(): String
+setContent(content: String): void
+getAuthor(): String
+setAuthor(author: String): void
+toString(): String
}
%% 实现 Cloneable 接口(Java 内置)
class Cloneable {
<>
}
%% 客户端示例类
class Client {
+main(args: String[]): void
}
%% 关系定义
Prototype <|-- Document : implements
Cloneable <|-- Document : implements
Client --> Document : uses
类 / 接口 | 模式中的角色 | 作用与职责 |
---|---|---|
Prototype |
抽象原型接口 | 声明克隆方法 clone() ,定义所有可克隆对象的通用接口,支持多态克隆。 |
Document |
具体原型类 | 实现 Prototype 接口和 Cloneable 接口,具体实现 clone() 方法,完成对象的浅拷贝。 |
Cloneable |
Java 标记接口 | 标记该类允许使用 Java 原生的 Object.clone() 机制,避免抛出 CloneNotSupportedException 。 |
Client |
客户端(Client) | 使用原型模式的客户端代码,创建原始对象并通过 clone() 方法快速复制新对象,无需重新构造。 |
Cloneable
是为了让 super.clone()
能正常工作(语言机制)。因为Object.clone()
是 protected
方法,只有实现了 Cloneable
接口的类才能安全地调用它而不抛异常。Prototype
是为了实现设计模式的抽象和多态(架构设计)。Prototype
不是强制要求,但定义一个克隆接口是良好实践,表现了它在软件设计和架构层面 的重要意义,它明确了该类支持克隆操作。Cloneable
是 Java 的底层克隆机制要求,Prototype
是设计模式的抽象需求。两者协同工作,一个负责"能克隆",一个负责"规范克隆"。
4. Java代码示例
1、原型接口(可选)
java
public interface Prototype {
Prototype clone();
}
2、具体原型类
java
public class Document implements Prototype, Cloneable {
private String title;
private String content;
private String author;
public Document(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public Document clone() {
try {
return (Document) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return "Document{" +
"title='" + title + ''' +
", content='" + content + ''' +
", author='" + author + ''' +
'}';
}
}
3、客户端
java
public class Client {
public static void main(String[] args) {
Document originalDoc = new Document("设计模式详解", "原型模式是一种创建型模式...", "Alice");
System.out.println("原始文档: " + originalDoc);
// 使用原型模式克隆对象
Document clonedDoc = (Document) originalDoc.clone();
System.out.println("克隆文档: " + clonedDoc);
// 验证两者是不同的对象实例
System.out.println("是否为同一对象? " + (originalDoc == clonedDoc)); // false
// 修改克隆对象,不影响原始对象(浅拷贝下,基本类型安全)
clonedDoc.setTitle("修改后的标题");
clonedDoc.setAuthor("Bob");
System.out.println("修改后原始文档: " + originalDoc);
System.out.println("修改后克隆文档: " + clonedDoc);
}
}
运行结果
ini
原始文档: Document{title='设计模式详解', content='原型模式是一种创建型模式...', author='Alice'}
克隆文档: Document{title='设计模式详解', content='原型模式是一种创建型模式...', author='Alice'}
是否为同一对象? false
修改后原始文档: Document{title='设计模式详解', content='原型模式是一种创建型模式...', author='Alice'}
修改后克隆文档: Document{title='修改后的标题', content='原型模式是一种创建型模式...', author='Bob'}
5. 优缺点
维度 | 优点(Pros) | 缺点(Cons) |
---|---|---|
性能 | 避免重复执行复杂的对象创建过程(如数据库连接、文件读取、复杂计算),直接复制已有对象,提升创建效率。 | 浅拷贝可能带来对象间状态共享问题;深拷贝实现复杂,可能涉及递归复制,影响性能。 |
封装性 | 客户端无需了解对象创建的内部细节,只需调用 clone() 方法,符合封装原则。 |
如果 clone() 实现不当(如未正确处理引用字段),会破坏封装,导致外部意外修改原型状态。 |
灵活性 | 可在运行时动态添加或替换可克隆的原型对象,支持插件化和配置化系统。 | 需要每个具体类都正确实现 clone() 方法,缺乏编译期强制约束,易遗漏或实现错误。 |
多态支持 | 通过 Prototype 接口统一操作不同类型的可克隆对象,支持多态克隆,便于集合管理。 |
必须定义接口或抽象类,增加了类层次结构的复杂性,小项目中可能显得冗余。 |
简化对象创建 | 无需通过 new 调用构造函数,尤其适用于构造参数复杂或难以获取的场景。 |
对于简单对象(如 POJO),使用构造函数更直观,原型模式反而增加复杂度。 |
语言机制依赖 | 利用 Java 的 Object.clone() (本地方法),性能较好。 |
必须实现 Cloneable 接口,否则抛异常;Cloneable 是"标记接口",语义不清晰,易被误用。 |
深拷贝挑战 | 支持通过重写 clone() 实现深拷贝,精确控制对象复制行为。 |
深拷贝需手动处理所有引用类型字段(如集合、自定义对象),实现繁琐且易出错。 |
6. 典型应用
- Spring 框架 的
@Scope("prototype")
每次从 Spring 容器获取该 Bean 时(如applicationContext.getBean("myBean")
),容器都会返回一个全新的实例 。这个实例不是通过反射调用构造函数重新创建的,而是在容器内部基于原型模板动态生成或克隆而来。
一句话总结:原型模式就是复制已有对象当模板,直接复印新对象,不用重新初始化。