原型模式的定義與特點
原型(Prototype
)模式的定義:
用一個已經創建的實例作爲原型,通過複製該原型對象來創建一個和原型相同或相似的新對象。在這裏,原型實例指定了要創建的對象的種類。用這種方式創建對象非常高效,根本無須知道對象創建的細節。
原型模式的結構
原型模式包含以下主要角色:
抽象原型類
:規定了具體原型對象必須實現的接口。
具體原型類
:實現抽象原型類的 clone() 方法,它是可被複制的對象。
訪問類
:使用具體原型類中的 克隆方法來複制新的對象。
原型模式的實現
原型模式的克隆分爲淺克隆
和深克隆
原型模式通常適用於以下場景:
1.對象之間相同或相似,即只是個別的幾個屬性不同的時候。
2.對象的創建過程比較麻煩,但複製比較簡單的時候。
淺克隆
Java 中的 Object 類提供了淺克隆的 clone() 方法,具體原型類只要實現 Cloneable
接口就可實現對象的淺克隆,這裏的 Cloneable 接口就是抽象原型類。其代碼如下:
具體原型類:
/**
* 學生類
*/
@Data
public class Student implements Cloneable {
private String name;//姓名
private int age;//年齡
private ArrayList<String> hobby;//愛好
/**
* 淺克隆方法
* @return
* @throws CloneNotSupportedException
*/
@Override
public Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
}
淺克隆測試類:
public class StudentTest {
@Test
public void test() throws Exception {
Student student = new Student();
student.setName("張三");
student.setAge(18);
Student student2 = student.clone();
System.out.println("原型對象:" + student);
System.out.println("克隆對象:" + student2);
System.out.println(student == student2);
}
}
運行結果:
看起來沒毛病啊,和我們預期一樣,克隆出一模一樣內容的對象,而且地址不相同。
其實是有個問題,暫時還沒暴露,下邊說說淺克隆帶來的問題。
淺克隆帶來的問題
注意看,上邊的測試代碼其實是沒有給愛好賦值的。我們來給他加點愛好看看就知道問題在哪了。
測試代碼:
public class StudentTest {
@Test
public void test() throws Exception {
Student student = new Student();
ArrayList<String> hobby = new ArrayList<String>();
hobby.add("書法");
hobby.add("畫畫");
student.setName("張三");
student.setAge(18);
student.setHobby(hobby);//給原型對象添加愛好
Student student2 = student.clone();
student2.getHobby().add("看書");//給克隆的對象增加一個愛好
System.out.println("原型對象:" + student);
System.out.println("克隆對象:" + student2);
System.out.println(student == student2);
}
}
運行結果:
發現問題沒?我明明是給克隆出來的對象增加了一個“看書”的愛好,怎麼原型對象的愛好也跟着變了呢?這就是淺克隆帶來的問題。
下邊就輪到深克隆出場來解決這個問題了。
深克隆
深克隆常見的實現方式有兩種:
序列化實現深克隆
具體原型類:
和之前不變,只需實現Serializable
接口新增深克隆方法就行。
@Data
public class Student implements Cloneable , Serializable {
private String name;//姓名
private int age;//年齡
private ArrayList<String> hobby;//愛好
/**
* 淺克隆方法
* @return
* @throws CloneNotSupportedException
*/
@Override
public Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
/**
* 序列化深克隆方法
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Student deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream ous = new ObjectOutputStream(bos);
ous.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Student)ois.readObject();
}
}
測試代碼:
public class StudentTest {
@Test
public void test() throws Exception {
Student student = new Student();
ArrayList<String> hobby = new ArrayList<String>();
hobby.add("書法");
hobby.add("畫畫");
student.setName("張三");
student.setAge(18);
student.setHobby(hobby);//給原型對象添加愛好
Student student2 = student.deepClone();//調用深克隆的方法
student2.getHobby().add("看書");//給克隆的對象增加一個愛好
System.out.println("原型對象:" + student);
System.out.println("克隆對象:" + student2);
System.out.println(student == student2);
}
}
測試結果:
json實現深克隆
具體原型類:
@Data
public class Student implements Cloneable , Serializable {
private String name;//姓名
private int age;//年齡
private ArrayList<String> hobby;//愛好
/**
* 淺克隆方法
* @return
* @throws CloneNotSupportedException
*/
@Override
public Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}
/**
* json深克隆方法
* @return
*/
public Student jsonDeepClone(){
String json = JSON.toJSONString(this);
Student student = JSON.parseObject(json, Student.class);
return student;
}
}
測試結果:也是沒毛病的
原型模式總結
優點:
1.性能優良,Java自帶的原型模式是基於內存二進制流的拷貝,比直接new一個對象的性能上提升了很多。
2.可以使用深克隆方式保存對象的狀態,使用原型模式將對象複製一份,將其保存起來,簡化了創建過程。
缺點:
1.必須要有克隆(或者拷貝)的方法
2.當對已有類進行改造時,需要修改代碼,違法開閉原則
注意:
深拷貝和淺拷貝需要運用得當