定义:
用原型实例指定创建对象的种类,并且 通过复制这些原型创建新的对象。
模式的结构中包括两种角色:
v 抽象原型 ( Prototype): 一个接口,负责定义对象复制自身的方法
v 具体原型 (Concrete Prototype): 实现 Prototype 接口的类。具体原型实现抽象原型中的方法,以便所创建的对象调用该方法复制自己。
深克隆实现:
v 实现 Cloneable 接口重写 clone 方法
java
import java.util.ArrayList;
import java.util.List;
// 可克隆接口
interface CloneablePrototype {
CloneablePrototype clone() throws CloneNotSupportedException;
}
// 具体原型类
class Prototype implements CloneablePrototype {
private int id;
private List<String> list;
public Prototype(int id, List<String> list) {
this.id = id;
this.list = list;
}
@Override
public Prototype clone() throws CloneNotSupportedException {
// 创建新的列表对象,并将原列表中的元素复制到新列表中
List<String> newList = new ArrayList<>(list);
// 创建新的原型对象并返回
return new Prototype(id, newList);
}
public void setId(int id) {
this.id = id;
}
public void addToList(String item) {
this.list.add(item);
}
public void displayInfo() {
System.out.println("ID: " + id);
System.out.println("List: " + list);
}
}
public class Main {
public static void main(String[] args) {
// 创建原型对象
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
Prototype original = new Prototype(1, list);
try {
// 进行深克隆
Prototype deepClone = original.clone();
// 修改克隆对象的属性
deepClone.setId(2);
deepClone.addToList("C");
// 显示原型对象和克隆对象的信息
System.out.println("Original:");
original.displayInfo();
System.out.println("Clone:");
deepClone.displayInfo();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在这个例子中,Prototype
类实现了 CloneablePrototype
接口,并提供了 clone()
方法来实现深度克隆。在 clone()
方法中,我们手动创建了一个新的列表对象,并将原列表中的元素复制到新列表中,然后使用新列表创建了一个新的原型对象并返回。
浅克隆实现:
v 利用序列化和反序列化技术
v 深克隆把引用的变量指向复制过的新的对象,而不是原有的被引用的对象
v 深克隆:让已实现 Clonable 接口的类中的属性也实现 Clonable 接口
v 基本数据类型能够自动实现深克隆(值的复制)
java
import java.util.ArrayList;
import java.util.List;
// 可克隆接口
interface CloneablePrototype {
CloneablePrototype clone() throws CloneNotSupportedException;
}
// 具体原型类
class Prototype implements CloneablePrototype {
private int id;
private List<String> list;
public Prototype(int id, List<String> list) {
this.id = id;
this.list = list;
}
@Override
public Prototype clone() throws CloneNotSupportedException {
// 使用默认的浅克隆方式,直接调用Object类的clone方法
return (Prototype) super.clone();
}
public void setId(int id) {
this.id = id;
}
public void addToList(String item) {
this.list.add(item);
}
public void displayInfo() {
System.out.println("ID: " + id);
System.out.println("List: " + list);
}
}
public class Main {
public static void main(String[] args) {
// 创建原型对象
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
Prototype original = new Prototype(1, list);
try {
// 进行浅克隆
Prototype shallowClone = original.clone();
// 修改克隆对象的属性
shallowClone.setId(2);
shallowClone.addToList("C");
// 显示原型对象和克隆对象的信息
System.out.println("Original:");
original.displayInfo();
System.out.println("Clone:");
shallowClone.displayInfo();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在这个示例中,Prototype
类实现了 CloneablePrototype
接口,并提供了 clone()
方法来实现浅克隆。在 clone()
方法中,我们直接调用了 Object
类的 clone()
方法,这是默认的浅拷贝实现,它会创建一个新对象,但不会复制对象内部的引用类型属性。
原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需知道其具体的实现细节。
浅克隆和深克隆的区别在于克隆对象内部包含的引用类型属性的处理方式:
浅克隆(Shallow Clone):
- 在浅克隆中,被克隆的对象和克隆产生的新对象共享内部的引用类型属性。
- 克隆产生的新对象拥有原对象内部引用类型属性的引用,而不是拷贝这些引用类型属性的内容。
- 如果原对象或克隆对象修改了共享的引用类型属性,那么另一个对象也会受到影响。
深克隆(Deep Clone):
- 在深克隆中,被克隆的对象和克隆产生的新对象拥有各自独立的内部引用类型属性。
- 克隆产生的新对象会复制原对象内部引用类型属性的内容,而不是共享这些引用类型属性的引用。
- 即使原对象或克隆对象修改了自己的引用类型属性,另一个对象也不会受到影响。
在实际应用中,根据具体需求和对象的结构,选择合适的克隆方式非常重要。通常情况下,如果对象的引用类型属性是不可变的(如 String
类型),那么使用浅克隆是安全的;但如果对象的引用类型属性是可变的,那么通常会选择深克隆来确保对象之间的独立性。
原型模式的优点和缺点如下:
优点:
- 简化对象创建:原型模式允许通过复制现有对象来创建新对象,避免了复杂对象的初始化过程,使得对象的创建过程更加简单和灵活。
- 提高性能:与通过调用构造函数创建对象相比,原型模式的性能通常更高,因为它避免了初始化过程中可能涉及的大量计算和I/O操作。
- 保护现有对象:原型模式在创建新对象时不会影响现有对象的状态,从而保护了现有对象的完整性。
- 动态配置对象:通过修改现有对象的属性,可以动态地配置新对象的属性,而无需修改创建新对象的代码。
- 减少子类数量:原型模式可以用来创建具有相同属性但不同行为的对象,从而减少了子类的数量,使得代码更加简洁和易于维护。
缺点:
- 深拷贝问题:当对象的属性包含复杂对象(例如集合或引用类型)时,需要进行深拷贝,否则新对象和原型对象之间可能会共享相同的引用,导致意外修改原型对象或新对象的属性。
- 循环引用问题:如果对象之间存在循环引用关系,那么复制对象时可能会导致无限递归,使得程序陷入死循环。
- 破坏封装性:原型模式可能会破坏对象的封装性,因为复制对象时通常需要访问对象的内部属性和方法,这使得对象的实现细节暴露给了外部。
- 复杂度增加:在处理深拷贝、循环引用等问题时,原型模式的实现可能会变得复杂,增加了代码的复杂度和维护成本。
综上所述,原型模式在适当的场景下可以带来很多优点,但也需要谨慎使用,特别是在处理对象的属性复杂或存在循环引用等情况时需要特别注意。
示例:
import java.util.ArrayList;
import java.util.List;
// 可克隆接口
interface CloneablePrototype {
CloneablePrototype clone() throws CloneNotSupportedException;
}
// 具体原型类
class Prototype implements CloneablePrototype {
private int id;
private List<String> list;
public Prototype(int id, List<String> list) {
this.id = id;
this.list = list;
}
@Override
public Prototype clone() throws CloneNotSupportedException {
// 浅克隆
// return new Prototype(id, list);
// 深克隆
List<String> newList = new ArrayList<>();
for (String item : list) {
newList.add(item);
}
return new Prototype(id, newList);
}
public void setId(int id) {
this.id = id;
}
public void addToList(String item) {
this.list.add(item);
}
public void displayInfo() {
System.out.println("ID: " + id);
System.out.println("List: " + list);
}
}
public class Main {
public static void main(String[] args) {
// 创建原型对象
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
Prototype original = new Prototype(1, list);
try {
// 浅克隆
// Prototype shallowClone = original.clone();
// shallowClone.setId(2);
// shallowClone.addToList("C");
// 深克隆
Prototype deepClone = original.clone();
deepClone.setId(2);
deepClone.addToList("C");
// 显示原型对象和克隆对象的信息
System.out.println("Original:");
original.displayInfo();
System.out.println("Clone:");
deepClone.displayInfo();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
运行结果:
在这个例子中,我们仍然使用了接口 CloneablePrototype
来表示可克隆对象,但没有使用继承关系。在 Prototype
类中,我们实现了 CloneablePrototype
接口,提供了深克隆和浅克隆两种克隆方法。在深克隆中,我们手动复制了原型对象中的列表属性,从而实现了深度克隆。