原型模式是用于创建重复的对象,同时又能保证性能。
在面向对象系统中,使用原型模式来复制一个对象自身,从而克隆出多个与原型对象一模一样的对象
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
1、细胞分裂
2、Java中的Object clone()方法
3、孙悟空拔毛变小猴
Prototype(抽象原型类)
抽象原型类是定义具有克隆自己的方法的接口,是所有具体原型类的公共父类,可以是抽象类,也可以是接口。ConcretePrototype(具体原型类)
具体原型类实现具体的克隆方法,在克隆方法中返回自己的一个克隆对象。Client(客户类)
客户类让==一个原型克隆自身==,从而创建一个新的对象。在客户类中只需要直接实例化或通过工厂方法等方式创建一个对象,再通过调用该对象的克隆方法复制得到多个相同的对象。
原型模式的克隆分为浅克隆和深克隆。
Object作为抽象原型类,在Java语言中,所有的类都是Object的子类,在==Object 中提供了克隆方法clone(),==用于创建一个原型对象,其 clone()方法具体实现由JVM完成,用户在使用时无须关心。
public class JingGu{
public void show(){
System.out.println("我是金箍棒");
}
}
Monkey类是具体原型类,也是Object类的子类。
在Java语言中,只有实现了Cloneable接口的类才能够使用clone()方法来进行复制,因此Monkey类实现了Cloneable接口。
在Monkey类中覆盖了Object的clone()方法,通过直接或者间接调用Object的clone()方法返回一个克隆的原型对象。在Monkey类中定义了一个成员对象jingGu,其类型为JingGu。
public class Monkey implements Cloneable {
private JingGu jingGu=null;
public Monkey(){
this.jingGu=new JingGu();
}
@Override
public Object clone(){
Monkey cloneMonkey =null;
try {
cloneMonkey=(Monkey) super.clone();
}catch (CloneNotSupportedException e){
System.out.println("克隆失败!");
}
return cloneMonkey;
}
public JingGu getJingGu(){
return this.jingGu;
}
}
在Client 客户端测试类中,比较原型对象和复制对象是否一致﹐并比较其成员对象jingGu的引用是否一致。
public class Client {
public static void main(String[] args) {
Monkey monkey,copyMonkey;
monkey=new Monkey();
copyMonkey= (Monkey) monkey.clone();
System.out.println("monkey == copyMonkey?");
System.out.println(monkey == copyMonkey); //结果为false
System.out.println("monkey.getJingGu() == copyMonkey.getJingGu()?");
System.out.println(monkey.getJingGu() == copyMonkey.getJingGu()); //结果为true
}
}
通过结果可以看出,表达式(monkey == copyMonkey)结果为false
即通过复制得到的对象与原型对象的引用不一致,也就是说明==在内存中存在两个完全不同的对象==,一个是原型对象,一个是克隆生成的对象。
但是表达式(monkey.getJingGu() == copyMonkey.getJingGu())结果为true,两个对象的成员对象是同一个,说明虽然对象本身复制了一份,但其==成员对象在内存中没有复制==,原型对象和克隆对象维持了对相同的成员对象的引用。
作为Monkey类的成员对象,在深克隆中,非基本数据类型对象也将被写入流中,因此非基本数据类型对象类也需要实现Serializable接口。
public class JingGu implements Serializable{
public void show(){
System.out.println("我是金箍棒");
}
}
Monkey作为具体原型类,由于实现的是深克隆,无须使用Object的 clone()方法,因此无须实现Cloneable接口;可以通过序列化的方式实现深克隆(代码中粗体部分),由于要将Monkey类型的对象写入流中,因此Monkey类需要实现Serializable接口。
public class Monkey implements Serializable {
private JingGu jingGu=null;
public Monkey(){
this.jingGu=new JingGu();
}
public Object deepClone() throws IOException,ClassNotFoundException, OptionalDataException{
//将对象写入流中
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos =new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis =new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois =new ObjectInputStream(bis);
return (ois.readObject());
}
public JingGu getJingGu(){
return this.jingGu;
}
}
在Client客户端测试类中,我们仍然比较深克隆后原型对象和拷贝对象是否一致,并比较其成员对象attachment的引用是否一致。
public class Client {
public static void main(String[] args) {
Monkey monkey,copyMonkey;
monkey=new Monkey();
copyMonkey= (Monkey) monkey.clone();
System.out.println("monkey == copyMonkey?");
System.out.println(monkey == copyMonkey); //结果为false
System.out.println("monkey.getJingGu() == copyMonkey.getJingGu()?");
System.out.println(monkey.getJingGu() == copyMonkey.getJingGu()); //结果为false
}
}
通过结果可以看出,表达式( email==copyEmail)结果为false,即通过复制得到的对象与原型对象的引用不一致,
表达式( email.getAttachment()==copyEmail.getAttachment())结果也为false,
原型对象与克隆对象对成员对象的引用不相同,说明其成员对象也复制了一份。
优缺点:原型模式最大的优点在于可以快速创建很多相同或相似的对象,简化对象的创建过程,还可以保存对象的一些中间状态。
其缺点在于需要为每一个类配备一个克隆方法,因此对已有类进行改造比较麻烦﹐需要修改其源代码,并且在实现深克隆时需要编写较为复杂的代码。
适用于:创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得。系统要保存对象的状态,而对象的状态变化很小,需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。