定义:
原型模式(Prototype Pattern)是一种创建型设计模式,它用于创建重复的对象,同时保持性能。这种模式的核心思想是通过复制一个已存在的实例来创建新的实例,而不是新建实例并对其进行初始化。原型模式适用于创建复杂对象的情况,特别是当对象创建的成本比较高时,如需要进行繁琐的资源消耗型操作(例如,数据库或文件的读取操作)。
原型模式通常涉及以下几个角色:
- 原型(Prototype) :
- 定义用于复制现有对象以生成新对象的接口。
- 具体原型(Concrete Prototype) :
- 实现原型接口的类,并提供复制自身的方法。这通常通过实现一个克隆方法(如Java中的
clone()
方法)来完成。
- 实现原型接口的类,并提供复制自身的方法。这通常通过实现一个克隆方法(如Java中的
- 客户(Client) :
- 创建新的对象时,客户端使用原型实例提供的克隆方法来获取新对象的副本,而不是直接通过
new
关键字创建。
- 创建新的对象时,客户端使用原型实例提供的克隆方法来获取新对象的副本,而不是直接通过
解决的问题:
- 高成本的对象创建 :
- 当创建一个对象的成本很高时,因为它需要进行复杂的初始化,如从数据库读取数据或进行复杂的计算。原型模式通过复制现有对象来避免这种高成本的创建过程。
- 避免复杂的构造过程 :
- 在某些情况下,对象的创建过程可能涉及多个步骤和要求,使用原型模式可以通过直接复制一个已经创建好的实例来简化创建过程。
- 动态添加或删除对象 :
- 在运行时动态地添加或删除具有特定配置的对象时,原型模式提供了一种方便的方法来复制配置相同的实例。
- 对象的解耦 :
- 有时,系统需要独立于其要创建对象的类。原型模式允许你复制一个对象,而不需要依赖于它们的具体类。
- 优化性能和内存 :
- 使用原型模式可以减少系统的整体资源消耗,因为复制通常比创建全新实例更轻量级。
使用场景:
- 性能敏感的对象创建 :
- 当对象的创建过程涉及昂贵的数据库操作、文件读取、复杂计算或网络调用等,而复制现有对象的成本相对较低时。
- 避免复杂的初始化步骤 :
- 如果一个对象的初始化过程非常复杂,如设置多个字段和依赖,使用原型模式可以通过克隆一个已经初始化的实例来避免这些复杂性。
- 类不容易获取或无法预知 :
- 当需要实例化的类在运行时才确定,或者类的实例化过程隐藏在一些我们无法访问的API后面时。
- 动态加载或生成对象 :
- 在需要根据当前环境或状态动态生成对象的场景中,可以通过复制预先存储的原型来实现。
- 大量相似对象的场景 :
- 当系统需要大量相似对象时,使用原型模式可以避免类初始化时的重复工作。
- 实现对象的解耦 :
- 当需要解耦系统中的对象创建和使用时,原型模式允许用户无需知道对象的具体类型就能创建新实例。
示例代码 1 - 浅拷贝实现:
java
public class ShallowPrototype implements Cloneable {
private String name;
public ShallowPrototype(String name) {
this.name = name;
}
// getter和setter
@Override
public ShallowPrototype clone() throws CloneNotSupportedException {
return (ShallowPrototype) super.clone();
}
}
示例代码 2 - 深拷贝实现:
java
public class DeepPrototype implements Cloneable {
private String name;
private SomeComplexObject complexObject;
public DeepPrototype(String name, SomeComplexObject complexObject) {
this.name = name;
this.complexObject = complexObject;
}
// getter和setter
@Override
public DeepPrototype clone() throws CloneNotSupportedException {
DeepPrototype cloned = (DeepPrototype) super.clone();
cloned.complexObject = new SomeComplexObject(this.complexObject); // 创建新的复杂对象实例
return cloned;
}
}
class SomeComplexObject {
private String data;
public SomeComplexObject(SomeComplexObject obj) {
this.data = obj.data;
}
// getter和setter
}
主要符合的设计原则:
- 开闭原则(Open-Closed Principle) :
- 原型模式支持开闭原则。一旦原型对象被创建并实现了克隆(Clone)方法,你可以通过克隆现有对象来添加新的对象实例,而无需修改现有的代码。
- 单一职责原则(Single Responsibility Principle) :
- 原型模式允许将对象创建和业务逻辑分离,使得每个类专注于单一的职责。原型对象专注于如何创建和复制自身的实例。
- 里氏替换原则(Liskov Substitution Principle) :
- 在原型模式中,任何继承自原型的新对象都应当能替代原型对象。这符合里氏替换原则,即程序中的对象应该能够被其子类对象所替换,而程序的逻辑不受影响。
原型模式主要通过实现开闭原则和单一职责原则来提高代码的可维护性和可扩展性。通过克隆方法,它允许在运行时动态地创建对象,提供了创建对象的灵活方式,同时避免了复杂的构造过程。
在JDK中的应用:
java.lang.Object
的clone()
方法 :- 在Java中,所有类都继承自
java.lang.Object
。Object
类提供了一个clone()
方法,可以用来复制对象。尽管这个方法默认是浅复制,但它提供了实现深复制的基础。
- 在Java中,所有类都继承自
- Java集合框架 :
- 许多Java集合类(如
ArrayList
,HashSet
,HashMap
等)实现了Cloneable
接口,提供了clone()
方法来创建集合的副本。这些集合类的克隆方法通常提供了集合内容的浅复制。
- 许多Java集合类(如
- 日期和时间对象 :
- 诸如
java.util.Date
这样的日期和时间相关类也实现了Cloneable
接口,允许通过克隆方法来创建日期对象的精确副本。
- 诸如
在Spring中的应用:
- Bean的原型作用域 :
- 在Spring框架中,Bean的作用域默认是单例(singleton),但可以配置为原型(prototype)。当一个Bean被定义为原型作用域时,每次通过容器请求这个Bean时,Spring容器都会创建一个新的Bean实例,而不是返回一个共享的单例实例。这正是原型模式的应用,即每次需要时都创建一个新的对象副本。
- 解决单例Bean的状态问题 :
- 在某些情况下,如果单例Bean包含了可变的数据字段,那么在并发环境下可能会出现数据安全问题。通过将这样的Bean定义为原型作用域,可以为每个请求创建一个新的实例,从而避免了状态共享的问题。
虽然Spring中原型作用域的应用并不广泛,但在需要独立状态或避免共享状态的场景中,原型模式提供了一种有效的解决方案。需要注意的是,与单例Bean相比,原型Bean的生命周期管理、依赖注入和销毁需要更多地由开发者来控制。