设计模式之原型模式
原型模式主要是为了解决需要创建多个相同的类时,提高其效率的一种模式。
假设我们现在要new10十只相同的🐏,我们用最蠢的方法:
sheep类:
package com.atguigu.prototype;
public class Sheep {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";
}
}
现在我们来复制🐏:
package com.atguigu.prototype;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//传统的方法
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//....
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
System.out.println(sheep5);
//...
}
}
五只意思意思一下,然而这种效率是非常低的。
下面我们使用原型模式:
object中有一个方法叫做clone,我们知道,Java中所有的类都是继承object的,不过你想使用这个方法,还需要自己重写。
//克隆该实例,使用默认的clone方法来完成
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return (Sheep)super.clone();
}
package com.atguigu.prototype.improve;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
System.out.println("原型模式完成对象的创建");
// TODO Auto-generated method stub
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.friend = new Sheep("jack", 2, "黑色");
Sheep sheep2 = (Sheep)sheep.clone(); //克隆
Sheep sheep3 = (Sheep)sheep.clone(); //克隆
Sheep sheep4 = (Sheep)sheep.clone(); //克隆
Sheep sheep5 = (Sheep)sheep.clone(); //克隆
}
}
这样就使用原型模式,效率要比一个一个new的效率要高。
如果我们在sheep类里面聚合一个羊类,这是羊的朋友:
public Sheep friend; //是对象, 克隆是会如何处理
然后再将其clone并打印就会发现被clone的这个羊朋友对象,是同一个对象,由此可见,clone这个方法对普通数据类型和String类型使用的是深拷贝,而对引用类型使用的是浅拷贝,解决这种问题有两种办法。首先我们new两个类:
package com.atguigu.prototype.deepclone;
import java.io.Serializable;
public class DeepCloneableTarget implements Serializable, Cloneable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package com.atguigu.prototype.deepclone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class DeepProtoType implements Serializable, Cloneable{
public String name; //String 属性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
//深拷贝 - 方式 1 使用clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和String的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType)deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
// TODO Auto-generated method stub
return deepProtoType;
}
//深拷贝 - 方式2 通过对象的序列化实现 (推荐)
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
}
第一种方法就是先将普通数据类型和String类型进行拷贝以后,在单独拿构造类型的clone方式再进行clone一次,
第二种方法就是使用IO将数据流拷贝到另一个容器当中,这种办法推荐使用。
值得一提的是,Spring的Ioc容器当中也使用了原型模式。