目录
- 1.概述
- 2.结构
- 3.实现
- 3.1.浅拷贝
- 3.2.深拷贝
- 3.2.1.通过对象序列化实现深拷贝(推荐)
- 3.2.2.重写 clone() 方法来实现深拷贝
- 4.优缺点
- 5.使用场景
1.概述
(1)原型模式 (Prototype Pattern) 是一种创建型设计模式,是通过复制(克隆)现有对象来创建新对象的模式。它允许我们创建一个原型对象,然后通过复制该原型对象来创建新的对象,而无需通过实例化类来创建新的对象。
(2)在原型模式中,一个类将自身的实例作为原型,通过复制这个原型来创建新的对象。这个过程隐藏了对象的创建细节,而且可以提高创建对象的效率。
(3)原型模式基于对象的复制,可以分为浅拷贝和深拷贝两种形式:
- 浅拷贝:复制对象时,仅仅复制对象的引用,而不复制对象本身。新对象和原对象共享同一个引用,修改一个对象会影响到另一个对象。
- 深拷贝:复制对象时,不仅复制对象的引用,还复制对象本身,创建一个独立的对象。新对象和原对象互不影响,修改一个对象不会影响到另一个对象。
(4)在实现原型模式时,通常需要实现一个 Cloneable
接口(或类似的机制),该接口标识类具有复制自己的能力。具体的实现细节则根据语言和框架的不同而有所差异。
2.结构
(1)原型模式包含如下角色:
- 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
(2)接口类图如下:
3.实现
Java 中的 Object 类中提供了 clone()
方法来实现浅克隆。Cloneable
接口是上面的类图中的抽象原型类,而实现了 Cloneable
接口的子实现类就是具体的原型类。
3.1.浅拷贝
Address.java
public class Address {
int id;
String addressName;
public Address(int id, String addressName) {
this.id = id;
this.addressName = addressName;
}
}
Person.java
public class Person implements Cloneable{
private int id;
private String name;
private Address address;
public String getName() {
return name;
}
public Person(int id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
System.out.println("具体的原型对象创建完成!");
}
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (Person) super.clone();
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
Client.java
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person(1, "小华", new Address(1, "北京"));
Person person2 = (Person) person1.clone();
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
System.out.println("person1.getName() == person2.getName() 的结果为: " + (person1.getName() == person2.getName()));
System.out.println("person1 == person2 的结果为: " + (person1 == person2));
}
}
输出结果如下图所示:
3.2.深拷贝
3.2.1.通过对象序列化实现深拷贝(推荐)
先将 Address 类和 Person 类实现 Serializable
接口,然后再修改 Client.java 中的代码即可。
Client.java
public class Client {
public static void main(String[] args) throws Exception {
Person person1 = new Person(1,"小华",new Address(1,"北京"));
//创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\testData\\a.txt"));
//写对象
oos.writeObject(person1);
//释放资源
oos.close();
//创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\testData\\a.txt"));
//读取对象
Person person2 = (Person) ois.readObject();
//释放资源
ois.close();
System.out.println("person1:"+person1);
System.out.println("person2:"+person2);
System.out.println("person1.getName() == person2.getName()的结果为:"+(person1.getName() == person2.getName()));
System.out.println("person1 == person2的结果为:"+(person1 == person2));
}
}
3.2.2.重写 clone() 方法来实现深拷贝
public class Person implements Cloneable {
private int id;
private String name;
private Address address;
public String getName() {
return name;
}
public Person(int id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
System.out.println("具体的原型对象创建完成!");
}
@Override
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
Person person = null;
person = (Person)super.clone();
//对引用数据类型单独处理
person.name = new String(name);
person.address = (Address)address.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
4.优缺点
(1)原型模式具有以下优点:
- 减少对象的创建成本:原型模式通过复制现有对象来创建新对象,避免了使用构造函数的开销,提高了对象的创建效率。
- 简化对象的创建过程:使用原型模式,不需要关心对象的具体实现细节,只需复制一个现有对象即可得到新的对象,简化了对象的创建过程。
- 支持动态添加和修改对象的属性:可以通过修改现有对象的属性来创建新对象,避免了在代码中显式设置对象属性的麻烦。
- 提供了一种可扩展的创建方式:原型模式通过复制现有对象,可以创建出多个具有相同属性的对象,也可以根据需要修改部分属性,从而具有更高的灵活性和可扩展性。
(2)原型模式也存在一些缺点:
- 需要实现 Cloneable 接口或类似的机制:在一些语言和框架中,实现原型模式需要实现 Cloneable 接口或类似的机制,这对于某些编程语言来说可能需要额外的工作。
- 深拷贝可能较为复杂:如果需要复制的对象存在引用类型的成员变量,深拷贝可能需要额外的处理来确保所有引用对象也能被正确地复制。
- 对象复杂性限制:对于包含循环引用或复杂对象图的对象,可能会导致复制过程变得复杂或不可行。
(3)综上所述,原型模式在某些情况下可以提供高效、灵活和可扩展的对象创建方式,但使用时需注意克隆的实现细节和对象的复杂性。
5.使用场景
(1)原型模式适用于以下场景:
- 对象创建成本高:当对象的创建成本较高时,例如需要进行复杂的计算、数据读取或网络请求等操作,可以使用原型模式复制现有对象来提高创建效率。
- 对象初始化复杂:当对象的初始化过程较为复杂,包含多个步骤或依赖关系较多时,可以使用原型模式来复制一个已经初始化好的对象,避免重新执行初始化过程。
- 动态创建和定制对象:当需要根据一些条件动态创建和定制对象时,原型模式可以提供一种更加灵活的创建方式。通过复制现有对象并根据需要修改部分属性,即可创建出符合条件的新对象。
- 避免构造函数调用:在某些场景下,使用构造函数创建对象可能不可行或不符合需要。例如,某些框架中对象的创建由框架负责,通过原型模式可以避免直接调用构造函数。
- 对象的状态变化较小:当对象的状态变化较小,只是部分属性需要修改时,使用原型模式可以避免重新创建对象,提高了性能。
(2)总的来说,原型模式适用于需要复制和创建新对象的场景,尤其是在对象的创建成本较高或初始化复杂的情况下,使用原型模式可以提高创建效率和灵活性。