定义
原型模式(Prototype Pattern)是一种创建型设计模式,用于通过复制现有对象来创建新对象,而无需依赖显式的构造函数。原型模式通过克隆已有对象来创建新对象,使得对象的创建更加灵活和高效。
步骤
Java 中的原型模式通常使用 Cloneable 接口和 clone() 方法来实现。具体步骤如下:
- 创建一个可被复制的原型类,并实现
Cloneable接口。 - 在原型类中实现
clone()方法,该方法会复制对象的属性值并返回一个新的副本。 - 在需要创建新对象的地方,通过调用原型对象的
clone()方法来获取一个新的对象副本。
优点:
- 可以动态创建对象,避免了显式的实例化过程。
- 可以简化对象创建过程,提高了创建对象的效率。
- 可以通过复制现有对象来创建新对象,避免了重复初始化的过程。
注意事项:
- 需要实现
Cloneable接口,并重写clone()方法。 - 在使用原型模式时,需要注意对象属性的克隆方式,确保复制的是属性的副本而不是引用。
- 原型模式适用于创建复杂对象或需要频繁创建相似对象的场景,不适用于创建简单对象或创建过程简单的场景。
Coding
下面是一个使用原型模式的示例,假设有一个 Cat 类:
@Data
public class Cat implements Cloneable{
private String name;
private Date birthday;
public Cat(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Cat cat = (Cat) super.clone();
//cat.birthday = (Date) cat.birthday.clone();
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}'+super.toString();
}
}
现在我们创建一个 Cat 对象并进行浅克隆:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Cat cat = new Cat("中华田园猫", new Date(666L));
Cat cat2 = (Cat) cat.clone();
System.out.println(cat1);
System.out.println(cat2);
cat.getBirthday().setTime(999999999999L);
System.out.println(cat1);
System.out.println(cat2);
}
}
输出结果如下:
Cat1{name='中华田园猫', birthday=Thu Jan 01 08:00:00 CST 1970}com.geely.design2.pattern.creational.prototype.clone.Cat@6ce5412
Cat2{name='中华田园猫', birthday=Thu Jan 01 08:00:00 CST 1970}com.geely.design2.pattern.creational.prototype.clone.Cat@6ce5412
Cat1{name='中华田园猫', birthday=Sun Sep 09 09:46:39 CST 2001}com.geely.design2.pattern.creational.prototype.clone.Cat@db73608f
Cat2{name='中华田园猫', birthday=Sun Sep 09 09:46:39 CST 2001}com.geely.design2.pattern.creational.prototype.clone.Cat@db73608f
浅克隆
可以看到,通过浅克隆创建的 cat2 对象与原始对象 cat1 共享相同的属性值。当我们修改 cat2 的属性时,原始对象 cat1 的属性也会被修改,因为它们引用相同的对象。
这就是浅克隆的特点:它仅复制对象的字段值,而不复制对象引用的其他对象。因此,如果对象包含对其他对象的引用,那么浅克隆只会复制引用,而不会创建新的独立对象。
深克隆
打开上面代码的注释
@Override
protected Object clone() throws CloneNotSupportedException {
Cat cat = (Cat) super.clone();
cat.birthday = (Date) cat.birthday.clone();
return cat;
}
再次执行刚才的 main方法,结果如下:
Cat1{name='中华田园猫', birthday=Thu Jan 01 08:00:00 CST 1970}com.geely.design2.pattern.creational.prototype.clone.Cat@6ce5412
Cat2{name='中华田园猫', birthday=Thu Jan 01 08:00:00 CST 1970}com.geely.design2.pattern.creational.prototype.clone.Cat@6ce5412
Cat1{name='中华田园猫', birthday=Thu Jan 01 08:00:00 CST 1970}com.geely.design2.pattern.creational.prototype.clone.Cat@6ce5412
Cat2{name='中华田园猫', birthday=Sun Sep 09 09:46:39 CST 2001}com.geely.design2.pattern.creational.prototype.clone.Cat@db73608f
可以看到,通过深克隆创建的对象修改属性后,对象发生改变。
可以看到,通过深克隆,cat2 对象是 cat1 对象的独立副本,对 cat1 的修改不会影响 cat2。在 cat1 中修改了生日,但是 cat2 保持了克隆时的原始状态,没有被修改。这就是深克隆与浅克隆的区别,深克隆创建了对象及其关联对象的完全独立副本。
破坏单例
原型模式在某些情况下可能会破坏单例模式的限制。单例模式是一种创建型设计模式,旨在确保一个类只有一个实例,并提供全局访问点。
当原型模式被应用于单例类时,复制原型对象可能会创建新的对象实例,从而破坏了单例的唯一性。这是因为原型模式通过复制已有对象来创建新对象,而不是通过类的构造函数进行实例化。
下面是一个示例,展示了原型模式如何破坏单例模式:
javaCopy codeclass Singleton implements Cloneable {
private static Singleton instance;
private Singleton() {
// 私有构造函数
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
try {
Singleton instance2 = (Singleton) instance1.clone();
System.out.println("Instance 1: " + instance1);
System.out.println("Instance 2: " + instance2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
输出结果:
graphqlCopy codeInstance 1: Singleton@5c29bfd
Instance 2: Singleton@15db9742
在上述示例中,我们创建了一个单例类 Singleton,并在 main() 方法中尝试通过原型模式复制单例对象。通过调用 instance1.clone() 方法,我们成功创建了 instance2,这导致了两个不同的对象实例。
原型模式破坏了单例模式的限制,因为原型模式复制了已有对象,创建了一个新的对象实例。这导致原本应该是唯一的单例对象出现了多个实例。
为了防止原型模式破坏单例模式,可以在原型类的 clone() 方法中抛出异常或返回单例对象本身,以确保无法复制单例对象。或者,在实现原型模式时,要小心在设计和使用上的细节,确保不会意外地破坏单例的唯一性。
应用场景
原型模式在项目中有许多应用场景,其中一些常见的场景包括:
- 对象的创建成本高:当创建一个对象需要复杂的计算或资源消耗时,使用原型模式可以通过复制现有对象来避免重复的计算或资源获取,从而提高性能。
- 对象的创建过程复杂:当对象的创建过程涉及多个步骤、依赖关系或配置参数时,使用原型模式可以避免这些复杂性,通过复制一个已有对象,可以快速获得一个相似的对象,而无需重新执行复杂的创建过程。
- 动态配置对象:原型模式可以用于创建可配置的对象实例。通过使用原型模式,可以先创建一个默认配置的对象实例,然后根据需要进行自定义配置,从而快速创建出符合不同需求的对象。
- 保护对象的不变性:有时候,需要确保对象的状态不会被修改。通过使用原型模式,可以在需要修改对象的状态时,复制一个对象作为副本,并对副本进行修改,而原始对象保持不变。这样可以避免对原始对象的意外修改。
- 快速生成大量相似对象:在某些场景下,需要大量相似的对象,但每个对象的属性稍有不同。通过使用原型模式,可以先创建一个原型对象,然后根据需要复制多个副本,并根据需求修改副本的属性,从而快速生成大量相似的对象。