0%

原型模式

原型模式(Prototype Pattern):通过克隆高效创建对象

原型模式是创建型设计模式的一种,核心思想是通过复制(克隆)现有对象(原型)来创建新对象,而非通过new关键字重新实例化。这种模式特别适合创建复杂对象或频繁创建相似对象的场景,能有效提高创建效率并简化流程。

原型模式的核心结构

原型模式的实现依赖两个关键要素,结构简洁但功能明确:

原型接口 / 类(Prototype)

  • 声明克隆自身的方法(通常名为clone())。
  • 在 Java 中,通常通过实现Cloneable接口(标识接口)并重写Object.clone()方法实现。

具体原型(Concrete Prototype)

  • 实现原型接口,具体定义克隆逻辑(深拷贝或浅拷贝)。
  • 示例:UserProduct等需要被克隆的类。

原型模式的实现步骤

在 Java 中实现原型模式需遵循以下步骤:

1. 实现Cloneable接口

Cloneable是一个标识接口(无任何方法),用于通知 JVM:该类可以被安全克隆。若未实现此接口,调用clone()会抛出CloneNotSupportedException

2. 重写Object.clone()方法

  • Object类的clone()方法是protected的,需重写为public,使其可被外部调用。
  • 该方法默认返回对象的浅拷贝,如需深拷贝需手动扩展。

浅拷贝与深拷贝

原型模式的核心是 “拷贝”,根据对对象内部引用类型的处理方式,分为浅拷贝深拷贝

1. 浅拷贝(Shallow Clone)

  • 定义:仅拷贝对象本身及基本类型成员,引用类型成员仍指向原对象的引用
  • 实现:直接调用super.clone()
代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 引用类型成员
class Address {
private String city;
public Address(String city) { this.city = city; }
// getter和setter
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
}

// 具体原型(实现浅拷贝)
class User implements 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;
}

// 重写clone(),实现浅拷贝
@Override
public User clone() throws CloneNotSupportedException {
return (User) super.clone(); // 仅拷贝基本类型和引用地址
}

// getter和setter
public Address getAddress() { return address; }
public String getName() { return name; }
}

// 测试浅拷贝
public class ShallowCloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("北京");
User user1 = new User("张三", 20, addr);

// 克隆user1
User user2 = user1.clone();

// 修改克隆对象的引用成员
user2.getAddress().setCity("上海");

// 原对象的引用成员也被修改(浅拷贝的问题)
System.out.println(user1.getAddress().getCity()); // 输出:上海
}
}
  • 问题:修改克隆对象的引用类型成员(如Address)会影响原对象,因为两者共享同一引用。

2. 深拷贝(Deep Clone)

  • 定义:不仅拷贝对象本身,所有引用类型成员也会被独立拷贝,原对象与克隆对象完全独立。
  • 实现方式:
    • 对引用类型成员手动克隆;
    • 通过序列化(Serializable)实现。
代码示例(手动克隆引用成员)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Address implements Cloneable {
private String city;
// 构造方法、getter、setter省略

// 引用类型自身也实现克隆
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}

class User implements Cloneable {
private String name;
private int age;
private Address address;

// 构造方法省略

// 重写clone(),实现深拷贝
@Override
public User clone() throws CloneNotSupportedException {
User clone = (User) super.clone();
// 手动克隆引用类型成员
clone.address = this.address.clone();
return clone;
}
}

// 测试深拷贝
public class DeepCloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("北京");
User user1 = new User("张三", 20, addr);

User user2 = user1.clone();
user2.getAddress().setCity("上海");

// 原对象不受影响(深拷贝成功)
System.out.println(user1.getAddress().getCity()); // 输出:北京
System.out.println(user2.getAddress().getCity()); // 输出:上海
}
}

原型模式的优缺点

优点

  1. 高效创建对象
    克隆通过直接复制内存二进制流实现,比new对象(需调用构造方法、初始化成员)更高效,尤其适合大对象或复杂对象。
  2. 简化创建流程
    隐藏了对象创建的细节,无需重新配置相同属性,直接通过原型克隆即可得到相似对象。
  3. 灵活性高
    可动态添加或删除原型,通过克隆扩展新对象,无需修改原有代码(符合开闭原则)。

缺点

  1. 克隆逻辑复杂
    深拷贝需手动处理所有引用类型成员,若对象嵌套层次深(如多级引用),克隆逻辑会非常繁琐。
  2. 与部分模式冲突
    • 克隆不会调用构造方法,因此无法与单例模式兼容(单例禁止多实例)。
    • 无法克隆final修饰的成员变量(final变量不可修改,克隆时无法赋值)。
  3. 维护成本高
    每个具体原型都需实现clone()方法,若类结构修改(如新增引用成员),需同步更新克隆逻辑。

适用场景

  1. 频繁创建相似对象
    如游戏中大量相似的敌人、粒子效果,通过克隆原型可减少重复初始化操作。
  2. 创建成本高的对象
    若对象需要数据库查询、网络请求或复杂计算才能初始化(如报表对象),克隆原型能显著提升性能。
  3. 避免构造函数副作用
    若构造方法包含副作用(如日志记录、资源申请),克隆可跳过这些操作,仅复制对象状态。
  4. 动态生成对象
    如插件系统中,通过克隆现有插件原型动态生成新插件实例,无需预知具体类型。

原型模式与其他模式的对比

模式 核心差异 适用场景
原型模式 通过克隆现有对象创建新对象 复杂对象、相似对象的快速创建
工厂方法模式 通过工厂类创建新对象,需指定类型 产品类型明确且易扩展
建造者模式 分步构建复杂对象,关注部件组装流程 多部件、多配置的复杂产品

总结

原型模式通过克隆机制实现了对象的高效创建,尤其适合处理 “创建成本高” 或 “频繁创建相似对象” 的场景。其核心是区分浅拷贝与深拷贝:浅拷贝简单但可能导致引用共享问题,深拷贝彻底独立但实现复杂。在实际开发中,需根据对象的成员类型(基本类型 / 引用类型)选择合适的拷贝方式,同时注意与其他模式(如单例)的兼容性。合理使用原型模式,可显著提升系统性能并简化对象创建逻辑

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10