这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
原型模式
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。同时又能保证性能;
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
结构
- 抽象原型类(Prototype):规定了具体原型对象必须实现的接口。必须具备两个条件:
- 1、实现Cloneable接口。
- 2、重写Object类中的clone方法
- 具体原型类(Concrete Prototype):实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类(Client):使用具体原型类中的 clone() 方法来复制新的对象。
演示
1、抽象原型类(Prototype)
public class Prototype implements Cloneable{
private int id;
private List<Item> list;
public Prototype(int id, List<Item> list) {
this.id = id;
this.list = list;
}
public Prototype clone() throws CloneNotSupportedException{
Prototype prototype = (Prototype)super.clone();
return prototype;
}
}
2、具体原型类(Concrete Prototype)
public class ClothesPrototype extends Prototype{
public ClothesPrototype(int id, List<Item> list) {
super(id, list);
}
}
3、访问类(Client)
public class Client {
public static void main(String[] args) throws CloneNotSupportedException{
ClothesPrototype cp = new ClothesPrototype(1, Arrays.asList(new Item()));
for(int i=0; i< 2; i++){
ClothesPrototype clonecp = (ClothesPrototype)cp.clone();
System.out.println(clonecp.getList());
System.out.println(clonecp);
// [com.example.pattern.prototype.Item@6996db8]
// com.example.pattern.prototype.ClothesPrototype@1963006a
// [com.example.pattern.prototype.Item@6996db8]
// com.example.pattern.prototype.ClothesPrototype@7fbe847c
}
}
}
总结
通过调用clone方法,可以成功拷贝出来一个对象;两个对象地址是不一样的,但是对象里的变量的地址是一样的。
出现这个问题的原因是因为拷贝区分深拷贝和浅拷贝
-
浅拷贝:被拷贝的对象的值跟原对象是一样的,且拷贝对象属性的值的引用地址也是原对象值的引用地址。所以浅拷贝是仅拷贝所考虑的对象
-
深拷贝:被拷贝的对象的值跟原对象是一样的,但是拷贝对象属性的值的引用地址与原对象值的引用地址是不一样的,所以深拷贝是对象且对象的值都重新新建一次。
实现深拷贝有两种方法
1.对象的属性值也实现clone方法
2.通过序列化把对象写入流再从流中取出来
优点
- Java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
使用场景
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。