【HeadFirst系列之HeadFirstJava】第18天之深入理解原型模式:从问题到解决方案(含 Java 代码示例)
深入理解原型模式:从问题到解决方案(含 Java 代码示例)
在软件开发中,我们经常需要创建对象,而有些对象的创建成本较高或者结构较为复杂。如何在不破坏封装的前提下,高效地创建对象? 这正是**原型模式(Prototype Pattern)**要解决的问题。
本篇文章将基于《Head First 设计模式》的内容,深入剖析原型模式的概念、应用场景、优缺点,并结合 JDK 和 Spring 框架的实际应用,提供详细的 Java 代码示例,帮助你掌握这一设计模式的精髓。
📌 1. 什么是原型模式?
原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制(克隆)已有对象来创建新的对象,而不是通过 new 关键字实例化对象。
它的核心思想是原型对象提供一个克隆方法,让客户端在需要创建对象时,直接调用该方法来获取副本。
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
📌 2. 传统对象创建的问题
在实际开发中,我们通常通过 new 关键字创建对象:
Person person1 = new Person("Alice", 25);
但当对象的初始化成本较高时(如涉及数据库查询、复杂计算等),反复 new 一个新对象会影响性能。
比如,一个数据库查询返回的用户对象,如果需要多次创建相同的用户对象,每次都去查询数据库,会造成资源浪费。
原型模式就是为了解决高成本对象的重复创建问题。
📌 3. 原型模式如何解决问题?
原型模式提供了一种对象克隆机制,让我们可以基于一个已有对象创建新对象,而不必重新执行复杂的初始化逻辑。
实现方式:
- Java 提供
Cloneable
接口,允许对象通过clone()
方法复制自身。 - 通过**浅拷贝(shallow copy)或深拷贝(deep copy)**实现克隆。
📌 4. Java 原型模式代码示例
我们用一个 “用户信息” 示例来演示原型模式。
🎯 1️⃣ 定义一个可克隆的类
class User implements Cloneable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
public void showUser() {
System.out.println("User: " + name + ", Age: " + age);
}
}
clone()
方法调用 super.clone()
,表示让 JVM 直接拷贝该对象。
🎯 2️⃣ 进行对象克隆
public class PrototypeDemo {
public static void main(String[] args) throws CloneNotSupportedException {
User user1 = new User("Alice", 25);
User user2 = (User) user1.clone(); // 克隆 user1
user1.showUser();
user2.showUser();
System.out.println("user1 == user2 ? " + (user1 == user2)); // false,表明是两个不同对象
}
}
运行结果:
User: Alice, Age: 25
User: Alice, Age: 25
user1 == user2 ? false
✅ 说明: 克隆后的 user2
和 user1
是两个不同的对象,但数据相同。
📌 5. 原型模式的应用场景
✅ 适用于:
- 创建对象成本较高的场景(如数据库查询、网络请求等)。
- 对象结构复杂,且需要避免 new 关键字重复创建。
- 系统需要提供多个类似对象,而彼此之间仅有少量差异。
🚫 不适用于:
- 类中包含复杂对象引用,且不支持深拷贝时(浅拷贝可能导致共享引用问题)。
- 对象的克隆逻辑过于复杂,甚至比直接 new 还要耗费性能。
📌 6. 深拷贝 vs 浅拷贝
浅拷贝(Shallow Copy):仅复制对象的基本属性,对于对象引用类型,仅复制内存地址(原对象和克隆对象共享同一个引用对象)。
深拷贝(Deep Copy):不仅复制基本属性,还创建新的对象实例,保证克隆后的对象互不影响。
🎯 深拷贝代码实现
class Address implements Cloneable {
private String city;
public Address(String city) { this.city = city; }
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, String city) {
this.name = name;
this.address = new Address(city);
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone(); // 深拷贝
return cloned;
}
}
深拷贝的关键是对引用对象也执行 clone()
方法,确保复制的是一个新的实例,而不是共享同一个对象。
📌 7. JDK 和 Spring 中的应用
🎯 JDK 应用
Object.clone()
:Java 语言中的clone()
方法就是原型模式的体现。ArrayList.clone()
:ArrayList
实现了Cloneable
接口,可通过clone()
方法创建副本。
🎯 Spring 框架中的应用
- Spring Bean 的原型模式(Prototype Scope)
Spring 提供的@Bean @Scope("prototype") public User user() { return new User("Alice", 25); }
prototype
作用域,每次获取 Bean 都会返回一个新实例,这与原型模式的思想一致。
📌 8. 原型模式的优缺点
✅ 优点
- 提高对象创建效率,避免重复初始化高成本对象。
- 封装克隆过程,客户端无需关注对象创建细节。
- 简化对象创建逻辑,减少
new
关键字的使用。
🚫 缺点
- 对象的拷贝可能涉及复杂的深拷贝逻辑,容易出错。
- 必须确保所有引用对象都支持克隆,否则容易出现共享引用问题。
- Java 的
Cloneable
接口设计较为陈旧,官方推荐使用 序列化 或者 手动拷贝构造函数 来代替clone()
方法。
📌 9. 总结
- 原型模式 通过克隆对象代替
new
关键字,适用于高成本对象的创建。 - Java 提供
Cloneable
接口支持浅拷贝,深拷贝需要手动实现。 - JDK 和 Spring 中都使用了原型模式,例如
clone()
方法、Spring 的 Prototype Scope。
💡 原型模式的核心: “用复制代替创建,提升对象创建效率!” 🚀