原型模式(Prototype Pattern)
原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。
工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动船舰的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()方法。
类似于:孙悟空吹猴毛,变出其他孙悟空。
原型模式UML类图
- Prototype: 原型类,声明一个克隆自己的接口
- ConcretePrototype: 具体的原型类,实现一个克隆自己的操作。
- Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
引入一个需求
创建一个Sheep对象,名称为:小羊,年龄为:2,颜色为:白色。创建出和小羊属性相同的5只羊。
传统方式
代码演示:
public class Sheep {
private String name;
private Integer age;
private String color;
public Sheep(String name,Integer age,String color){
this.name = name;
this.age = age;
this.color = color;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
}
//创建
public static void main(String[] args) {
Sheep sheep = new Sheep("小羊",2,"白色");
Sheep sheep1 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
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());
}
传统方式优缺点说明:
- 优点是:比较好理解,简单易操作。
- 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低。
- 总是需要重新初始化对象,而不是动态的获得对象运行时的状态,不够灵活。
原型模式方式
实例代码:
public class Sheep implements Cloneable{
private String name;
private Integer age;
private String color;
public Sheep friend; //增加一个对象属性
public Sheep(String name,Integer age,String color){
this.name = name;
this.age = age;
this.color = color;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "name="+name + "age=" +age + "color=" +color;
}
}
//调用
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("小羊",2,"白色");
sheep.friend = new Sheep("小羊对象属性",2,"黑色");
Sheep sheep1 = (Sheep) sheep.clone();
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
Sheep sheep5 = (Sheep) sheep.clone();
System.out.println(sheep1 + " firend=" + sheep1.friend);
System.out.println(sheep2 + " firend=" + sheep1.friend);
System.out.println(sheep3 + " firend=" + sheep1.friend);
System.out.println(sheep4 + " firend=" + sheep1.friend);
System.out.println(sheep5 + " firend=" + sheep1.friend);
}
运行结果如下:
原型模式优缺点说明:
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
- 不用重新初始化对象,而是动态的获取对象运行时的状态。
- 如果原始对象发生变化(增加或者减少属性),其他克隆对象的也会发生相应的变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码
- 需要为每一个类配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,需要修改其源码,违背了OCP原则,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
深入讨论-浅拷贝和深拷贝
浅拷贝介绍
- 对于数据类型是基本类型的成员变量,浅拷贝回直接进行值传递,也就是将该属性值复制一份给新的对象。
- 对于数据类型是引用类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝回进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
- 浅拷贝是使用默认的clone()方法来实现。
查看上述需求对象各个属性地址
运行代码:
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("小羊",2,"白色");
sheep.friend = new Sheep("小羊对象属性",2,"黑色");
Sheep sheep1 = (Sheep) sheep.clone();
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
Sheep sheep5 = (Sheep) sheep.clone();
System.out.println(sheep +" " + sheep.getName().hashCode() +" firend="+ sheep.friend.hashCode());
System.out.println(sheep1 +" "+ sheep1.getName().hashCode() + " firend=" + sheep1.friend.hashCode());
System.out.println(sheep2 +" "+ sheep2.getName().hashCode() + " firend=" + sheep2.friend.hashCode());
System.out.println(sheep3 +" "+ sheep3.getName().hashCode() + " firend=" + sheep3.friend.hashCode());
System.out.println(sheep4 +" "+ sheep4.getName().hashCode() + " firend=" + sheep4.friend.hashCode());
System.out.println(sheep5 +" "+ sheep5.getName().hashCode() + " firend=" + sheep5.friend.hashCode());
}
运行结果
深拷贝介绍
- 复制对象的所有基本数据类型的成员变量值。
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。
实现方式有两种:
- 重写clone方法来实现
- 通过对象序列化来实现深拷贝(推荐使用)
方式一代码实现:
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.cloneClass = cloneClass;
this.cloneName = cloneName;
}
//因为该类的属性都是基本类型,使用默认clone 即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class DeepProtoType implements Serializable,Cloneable {
private static final long serialVersionUID = 1L;
public String name;
public DeepCloneableTarget deepCloneableTarget;
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
deep = super.clone();
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deepProtoType;
}
}
//调用
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType p = new DeepProtoType();
p.name = "张三";
p.deepCloneableTarget = new DeepCloneableTarget("大三","小三");
DeepProtoType p1 = (DeepProtoType) p.clone();
System.out.println("p.name=" + p.name.hashCode() + "p.deepCloneableTarget="+p.deepCloneableTarget.hashCode());
System.out.println("p1.name=" + p1.name.hashCode() + "p1.deepCloneableTarget="+p1.deepCloneableTarget.hashCode());
}
运行结果:
方式二代码实现:
public Object deepClone(){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try{
//序列化
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType o = (DeepProtoType) ois.readObject();
return o;
}catch (Exception ex){
ex.printStackTrace();
return null;
}finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//调用
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType p = new DeepProtoType();
p.name = "张三";
p.deepCloneableTarget = new DeepCloneableTarget("大三","小三");
DeepProtoType p1 = (DeepProtoType) p.deepClone(); //调用方法
System.out.println("p.name=" + p.name.hashCode() + " p.deepCloneableTarget="+p.deepCloneableTarget.hashCode());
System.out.println("p1.name=" + p1.name.hashCode() + " p1.deepCloneableTarget="+p1.deepCloneableTarget.hashCode());
}
运行结果:
感谢阅读