原型模式也屬于創造型設計模式,他是通過復制一個已經存在的對象實例而得到一個新的實例,避免繁瑣的實例化過程。在這里被復制的對象實例叫做原型,復制原型也叫克隆對象,有深克隆和淺克隆兩種。
淺克隆
對值類型的成員變量進行值的復制,對引用類型的成員變量只復制引用,不復制引用的對象;也就是值類型和引用進行復制
深克隆
在淺克隆的基礎上,對引用類型也進行克隆,而不僅僅是引用
原型克隆的 UML 類圖如下:
client:提出創建對象請求;
propotype:抽象原型,給出所有具體原型需要實現的接口;
concretePropotype:具體的原型對象,需要被復制的對象;
仍然加以代碼實現
先來淺拷貝,抽象原型
public interface Prototype extends Cloneable {
public Object clone() ;
}
//具體克隆對象
public class ConcretePropotype implements Propotype {
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
客戶端測試下
public class Client {
public static void main(String[] args) {
ConcretePropotype propotype=new ConcretePropotype();
propotype.setName("zhangsan");
propotype.setAge(11);
ConcretePropotype propotypeClone=(ConcretePropotype) propotype.clone();
System.out.println(propotypeClone.getName());
propotypeClone.setName("lisi");
propotypeClone.setAge(12);
System.out.println(propotype.getAge());
System.out.println(propotypeClone.getName());
}
}
這個屬于淺克隆,沒有涉及到引用類型,為了驗證下,繼續引入一個對象Student
public class Student {
private String name;
private int age;
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 class ConcretePropotype2 implements Propotype {
private String name;
private int age;
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
進入 Client 端進行驗證一下
public class Client2 {
public static void main(String[] args) {
ConcretePropotype2 propotype=new ConcretePropotype2();
Student stu=new Student();
stu.setAge(50);
stu.setName("東郭");
propotype.setStudent(stu);
ConcretePropotype2 propotypeClone=(ConcretePropotype2) propotype.clone();
propotypeClone.getStudent().setName("西施");
propotypeClone.getStudent().setAge(100);
System.out.println("原型:"+propotype.getStudent().getName());
System.out.println("克隆后:"+propotypeClone. getStudent().getName());
System.out.println("原型:"+propotype.getStudent().getAge());
System.out.println("克隆后:"+propotypeClone. getStudent().getAge());
}
}
由于是淺克隆,只是引用復制了,所以克隆后把原來的對象也修改了,最后的結果是
原型:西施
克隆后:西施
原型:100
克隆后:100
這個情況就會出現很大問題,把原來的對象徹底修改了,這可是不想遇到的,此時,深克隆粉墨登場,來解決這個問題;但是為了深克隆,需要克隆的對象可序列化,之前的Student
對象需要實現public class Student implements Serializable
,這樣在 clone 對象里面利用串行化來做深復制。
public Object deepClone(){
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
//將對象從流里面讀出來
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return oi.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
當然還可以逐個對象的引用進行復制,那樣引用層次較淺還可以接受,太深的話操作性非常不好。還是建議利用串行化來做深復制
。
深度克隆之后完全就是兩個對象了,互相不干擾,但需要克隆的對象序列化。既然是從克隆出來的,所以依附就小多了,只要產品具有克隆方法就可以克隆一個新的自己出來,再加以演化,就可以很方便的造出不同級別的產品出來。唯一的難點就是何時何地增加克隆方法,以及不能克隆的屬性加以transient
標識。