原型模式 (Prototype Pattern)
通过复制现有对象来创建新对象,避免重复的初始化操作
目录
概述
原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是从头开始创建。
适用场景
- 对象创建成本高(需要复杂计算或数据库操作)
- 需要创建与现有对象相似的对象
- 运行时动态决定创建哪种对象
基础实现
实现 Cloneable 接口
// 原型接口
public interface Prototype extends Cloneable {
Prototype clone();
}
// 具体原型类
public class Document implements Cloneable {
private String title;
private String content;
private List<String> tags;
public Document(String title, String content, List<String> tags) {
this.title = title;
this.content = content;
this.tags = tags;
}
// 浅拷贝
@Override
public Document clone() {
try {
return (Document) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
// Getters and Setters...
}
// 使用
Document template = new Document("模板", "内容", Arrays.asList("标签1"));
Document doc1 = template.clone();
doc1.setTitle("文档1");深拷贝 vs 浅拷贝
浅拷贝问题
// 浅拷贝:引用类型共享
Document doc1 = template.clone();
doc1.getTags().add("新标签");
// template 的 tags 也会被修改!深拷贝实现
public class Document implements Cloneable, Serializable {
private String title;
private String content;
private List<String> tags;
private Author author;
// 深拷贝方式 1:手动复制
@Override
public Document clone() {
try {
Document cloned = (Document) super.clone();
// 复制引用类型
cloned.tags = new ArrayList<>(this.tags);
cloned.author = this.author.clone();
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
// 深拷贝方式 2:序列化
public Document deepClone() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Document cloned = (Document) ois.readObject();
ois.close();
return cloned;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// Author 类也需要实现 Cloneable
public class Author implements Cloneable, Serializable {
private String name;
private String email;
@Override
public Author clone() {
try {
return (Author) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}使用拷贝构造函数
public class Document {
private String title;
private String content;
private List<String> tags;
// 普通构造
public Document(String title, String content, List<String> tags) {
this.title = title;
this.content = content;
this.tags = new ArrayList<>(tags);
}
// 拷贝构造(深拷贝)
public Document(Document other) {
this.title = other.title;
this.content = other.content;
this.tags = new ArrayList<>(other.tags);
}
}
// 使用
Document doc1 = new Document("原文档", "内容", tags);
Document doc2 = new Document(doc1); // 深拷贝
doc2.setTitle("新文档");原型注册表
// 原型管理器
public class PrototypeRegistry {
private static final Map<String, Prototype> prototypes = new HashMap<>();
static {
// 注册默认原型
prototypes.put("default", new Report("默认报告", "模板内容"));
prototypes.put("monthly", new Report("月度报告", "月度数据..."));
prototypes.put("annual", new Report("年度报告", "年度总结..."));
}
public static void register(String key, Prototype prototype) {
prototypes.put(key, prototype);
}
public static Prototype create(String key) {
Prototype prototype = prototypes.get(key);
if (prototype == null) {
throw new IllegalArgumentException("未知原型: " + key);
}
return prototype.clone();
}
}
// 使用
Report report = (Report) PrototypeRegistry.create("monthly");
report.setTitle("2024年1月月报");框架中的应用
Spring 原型作用域
@Component
@Scope("prototype")
public class PrototypeBean {
private String data;
public void setData(String data) {
this.data = data;
}
}
// 使用
@Configuration
public class AppConfig {
@Autowired
private ApplicationContext context;
public void usePrototype() {
// 每次获取都是新实例
PrototypeBean bean1 = context.getBean(PrototypeBean.class);
PrototypeBean bean2 = context.getBean(PrototypeBean.class);
System.out.println(bean1 == bean2); // false
}
}JSON 对象的复制
import com.fasterxml.jackson.databind.ObjectMapper;
public class DeepCopyUtil {
private static final ObjectMapper mapper = new ObjectMapper();
public static <T> T copy(T object, Class<T> clazz) {
try {
String json = mapper.writeValueAsString(object);
return mapper.readValue(json, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 使用
User user1 = new User("john", 25);
User user2 = DeepCopyUtil.copy(user1, User.class);实际应用:报表生成
public abstract class ReportTemplate implements Cloneable {
protected String title;
protected String header;
protected String footer;
protected List<String> sections;
public ReportTemplate(String title) {
this.title = title;
this.sections = new ArrayList<>();
}
// 添加内容
public void addSection(String section) {
sections.add(section);
}
// 生成报告
public String generate() {
StringBuilder report = new StringBuilder();
report.append(header).append("\n");
report.append("标题: ").append(title).append("\n");
for (String section : sections) {
report.append(section).append("\n");
}
report.append(footer);
return report.toString();
}
@Override
public ReportTemplate clone() {
try {
ReportTemplate cloned = (ReportTemplate) super.clone();
cloned.sections = new ArrayList<>(this.sections);
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
// 月度报告
public class MonthlyReport extends ReportTemplate {
public MonthlyReport() {
super("月度报告");
this.header = "===== 月度报告 =====";
this.footer = "===== 报告结束 =====";
}
}
// 使用
MonthlyReport template = new MonthlyReport();
template.addSection("销售额: 100万");
template.addSection("用户数: 5000");
// 克隆并定制
MonthlyReport janReport = template.clone();
janReport.addSection("月份: 1月");
MonthlyReport febReport = template.clone();
febReport.addSection("月份: 2月");深拷贝方式对比
| 方式 | 优点 | 缺点 |
|---|---|---|
| clone() | 标准方法 | 需要实现 Cloneable |
| 序列化 | 自动深拷贝 | 性能较差 |
| 拷贝构造 | 简单直观 | 需要手动复制每个字段 |
| JSON 复制 | 通用性强 | 依赖库,性能一般 |
| BeanUtils | 方便 | 性能较差,部分字段可能无法复制 |
优缺点
| 优点 | 缺点 |
|---|---|
| 避免子类化 | 复杂对象的深拷贝实现困难 |
| 动态添加/删除产品 | 需要实现克隆方法 |
| 减少初始化代码 | 深拷贝可能有循环引用问题 |
最佳实践
- 优先使用拷贝构造:比 clone() 更清晰
- 注意深拷贝问题:特别是集合和对象引用
- 不可变对象:可以安全使用浅拷贝
- 原型注册表:管理常用原型对象