原型模式 (Prototype Pattern)
什么是原型模式?
原型模式是一种创建型设计模式,它允许你通过复制现有对象来创建新对象,而不是通过实例化类来创建。
简单来说:原型模式就是通过克隆(复制)现有对象来创建新对象。
生活中的例子
想象一下:
- 复印文档:复印一份已有的文档,而不是重新写一份
- 克隆生物:克隆一只羊,而不是重新培育一只
- 复制粘贴:复制一段文字,而不是重新输入
为什么需要原型模式?
传统方式的问题
java
// 创建对象
User user1 = new User("张三", 25);
User user2 = new User("张三", 25); // 需要重新设置属性
问题:
- 重复代码:需要重复设置相同的属性
- 创建复杂:如果对象创建过程复杂,需要重复执行
- 性能开销:如果对象创建耗时,会影响性能
原型模式的优势
java
// 克隆对象
User user1 = new User("张三", 25);
User user2 = user1.clone(); // 直接克隆,无需重新设置属性
优势:
- 简化创建:通过克隆简化对象的创建
- 提高性能:避免重复执行复杂的创建过程
- 动态创建:可以在运行时动态创建对象
原型模式的结构
┌─────────────────────┐
│ Prototype │ 原型接口
├─────────────────────┤
│ + clone(): Object │
└──────────┬──────────┘
│ 实现
├──┬──────────────────┬──────────────┐
│ │ │
┌──────────┴──────┐ ┌───────────┴───────┐ ┌───┴────────┐
│ ConcreteProto1 │ │ ConcreteProto2 │ │ ... │ 具体原型
├─────────────────┤ ├───────────────────┤ ├────────────┤
│ + clone(): Obj │ │ + clone(): Obj │ │ │
└─────────────────┘ └───────────────────┘ └────────────┘
代码示例
1. 定义原型接口
java
/**
* 原型接口
*/
public interface Prototype {
/**
* 克隆方法
*/
Prototype clone();
}
2. 定义地址类(引用类型)
java
/**
* 地址类(引用类型,用于演示浅拷贝和深拷贝)
*/
public class Address implements Cloneable {
private String city;
private String district;
public Address(String city, String district) {
this.city = city;
this.district = district;
}
// Getter和Setter方法
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDistrict() {
return district;
}
public void setDistrict(String district) {
this.district = district;
}
@Override
public String toString() {
return city + "市" + district;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
3. 实现具体原型
java
/**
* 用户类(实现原型接口和Cloneable接口)
*/
public class User implements Prototype, Cloneable {
private String name;
private int age;
private Address address;
public User(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
/**
* 浅拷贝
*/
@Override
public User clone() {
try {
return (User) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
/**
* 深拷贝
*/
public User deepClone() {
try {
User user = (User) super.clone();
user.address = (Address) address.clone();
return user;
} catch (CloneNotSupportedException e) {
return null;
}
}
// Getter和Setter方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Address getAddress() {
return address;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
4. 使用原型
java
/**
* 原型模式测试类
* 演示浅拷贝和深拷贝的区别
*/
public class PrototypeTest {
public static void main(String[] args) {
System.out.println("=== 原型模式测试 ===\n");
// 创建原始对象
Address address = new Address("北京", "朝阳区");
User originalUser = new User("张三", 25, address);
System.out.println("原始用户: " + originalUser);
System.out.println("\n--- 浅拷贝 ---");
User shallowCopy = originalUser.clone();
System.out.println("浅拷贝用户: " + shallowCopy);
System.out.println("原始用户 == 浅拷贝用户: " + (originalUser == shallowCopy));
System.out.println("原始地址 == 浅拷贝地址: " + (originalUser.getAddress() == shallowCopy.getAddress()));
// 修改浅拷贝的地址
shallowCopy.getAddress().setCity("上海");
System.out.println("\n修改浅拷贝的地址后:");
System.out.println("原始用户: " + originalUser);
System.out.println("浅拷贝用户: " + shallowCopy);
System.out.println("注意:原始用户的地址也被修改了(浅拷贝问题)");
System.out.println("\n--- 深拷贝 ---");
User deepCopy = originalUser.deepClone();
System.out.println("深拷贝用户: " + deepCopy);
System.out.println("原始用户 == 深拷贝用户: " + (originalUser == deepCopy));
System.out.println("原始地址 == 深拷贝地址: " + (originalUser.getAddress() == deepCopy.getAddress()));
// 修改深拷贝的地址
deepCopy.getAddress().setCity("广州");
System.out.println("\n修改深拷贝的地址后:");
System.out.println("原始用户: " + originalUser);
System.out.println("深拷贝用户: " + deepCopy);
System.out.println("注意:原始用户的地址没有被修改(深拷贝正确)");
System.out.println("\n=== 原型模式的优势 ===");
System.out.println("1. 简化创建:通过克隆简化对象的创建");
System.out.println("2. 提高性能:避免重复执行复杂的创建过程");
System.out.println("3. 动态创建:可以在运行时动态创建对象");
System.out.println("4. 隐藏细节:隐藏对象创建的细节");
}
}
浅拷贝 vs 深拷贝
浅拷贝
只复制基本类型和对象的引用,不复制引用对象本身。
java
public class Address implements Cloneable {
private String city;
// ...
}
public class User implements Cloneable {
private String name;
private Address address; // 引用类型
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝,只复制address的引用
}
}
问题:修改克隆对象的address会影响原对象。
深拷贝
复制所有对象,包括引用对象。
java
public class User implements Cloneable {
private String name;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User) super.clone();
user.address = (Address) address.clone(); // 深拷贝
return user;
}
}
原型模式的优点
- 简化创建:通过克隆简化对象的创建
- 提高性能:避免重复执行复杂的创建过程
- 动态创建:可以在运行时动态创建对象
- 隐藏细节:隐藏对象创建的细节
原型模式的缺点
- 深拷贝复杂:实现深拷贝可能比较复杂
- 类依赖:类必须实现Cloneable接口
- 循环引用:处理循环引用比较困难
适用场景
- 对象创建复杂:对象的创建过程复杂或耗时
- 对象相似:需要创建多个相似的对象
- 保护性拷贝:需要保护原对象不被修改
- 动态创建:需要在运行时动态创建对象
常见应用场景
- 对象池:对象池中的对象克隆
- 文档编辑:复制粘贴文档内容
- 游戏开发:克隆游戏角色或道具
- 配置管理:复制配置对象
使用建议
- 对象创建复杂:使用原型模式
- 需要多个相似对象:使用原型模式
- 简单对象:直接使用构造函数即可
注意事项
⚠️ 原型模式虽然有用,但要注意:
- 区分浅拷贝和深拷贝
- 处理循环引用
- 考虑使用序列化实现深拷贝