創建型模式之原型模式(深拷貝/淺拷貝)

原型模式的定義與特點

原型(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.當對已有類進行改造時,需要修改代碼,違法開閉原則
注意:
深拷貝和淺拷貝需要運用得當

<<上一篇:單例模式
>>下一篇:工廠方法模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章