設計模式之原型模式(Prototype Pattern)

What

是用于創(chuàng)建重復的對象,同時又能保證性能的一種創(chuàng)建型模式。

Why

優(yōu)點:
1、性能提高。當創(chuàng)建新的對象比較復雜時,可以利用原型模式簡化對象的創(chuàng)建過程,同時也能夠提高效率。

因為通過clone創(chuàng)建的一個對象比直接用new創(chuàng)建對象更有效率。

2、逃避構造函數(shù)的約束。

缺點:

  1. 在實現(xiàn)深克隆的時候可能需要比較復雜的代碼。
  2. 需要為每一個類配備一個克隆方法,而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事,必須修改其源代碼,違背了“開閉原則”。

Where

  1. 當通過new產(chǎn)生一個對象需要非常繁瑣的數(shù)據(jù)準備或訪問權限。
  2. 一個對象多個修改者的場景。
  3. 類初始化需要消化非常多的資源,這個資源包括數(shù)據(jù)、硬件資源等。
  4. 資源優(yōu)化場景。

How

  1. 實現(xiàn)Cloneable接口
  2. 重寫clone方法

示例:一個班級有多個學生,每個學生只對應一個班級。
學生類:

public class Student implements Cloneable {
    private String name;
    private Integer age;
    private Classroom classroom;

    public Student() {
        System.out.println("Student類的無參構造器...");
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("{");
        sb.append("\"Name\":\"")
                .append(name).append('\"');
        sb.append(",\"Age\":")
                .append(age);
        sb.append(",\"Classroom\":")
                .append(classroom);
        sb.append('}').append("    ").append(super.toString());
        return sb.toString();
    }

    //省略get、set方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

課室類(Classroom):

public class Classroom{

    private String className;

    public Classroom() {
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("{");
        sb.append("\"ClassName\":\"")
                .append(className).append('\"');
        sb.append('}').append(super.toString());
        return sb.toString();
    }

    //省略get、set方法
}

測試類:

public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student();
        Classroom classroom = new Classroom();
        classroom.setClassName("高三<7>班");
        student1.setName("Jone");
        student1.setAge(20);
        student1.setClassroom(classroom);
        Student student2 = (Student) student1.clone();
        Student student3 = (Student) student1.clone();

        //先觀察clone出來的對象地址
        System.out.println("各個對象的地址");
        System.out.println(student1.toString());
        System.out.println(student2.toString());
        System.out.println(student3.toString());


        System.out.println("修改student2和student3的屬性");
        student2.setName("Marry");
        student2.setAge(18);
        student3.setName("Stan");
        student3.setAge(19);
        System.out.println(student1);
        System.out.println(student2);
        System.out.println(student3);

        System.out.println("Marry被調(diào)到高三<1>班");
        student2.getClassroom().setClassName("高三<1>班");
        System.out.println(student1);
        System.out.println(student2);
        System.out.println(student3);
    }
Student類的無參構造器...
各個對象的地址
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@74a14482
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@1540e19d
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@677327b6
修改student2和student3的屬性
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@1540e19d
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@677327b6
Marry被調(diào)到高三<1>班
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@1540e19d
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@677327b6

對于輸出結果,有下面幾個點值得注意:
1.clone對象并不會調(diào)用對象的構造方法。
2.clone出來的對象地址都不一樣,可以說明當執(zhí)行完clone方法的時候,會在堆內(nèi)存中開辟出一塊內(nèi)存給新對象。
3.clone出來的對象屬性都一樣,包括引用對象的內(nèi)存地址都一樣。所以當修改任意對象的引用變量的時候,會把所有含有被修改的引用變量都更改。

Show clone method in heap and stack

關于第三點,其實就是經(jīng)典的一個話題:深克隆和淺克隆。

淺克隆:創(chuàng)建一個新對象,新對象的屬性和原來對象完全相同,對于非基本類型屬性,仍指向原有屬性所指向的對象的內(nèi)存地址。
深克隆:創(chuàng)建一個新對象,屬性中引用的其他對象也會被克隆,不再指向原有對象地址。

很明顯,上面的例子是淺克隆。那深克隆如何做到呢?
首先需要修改Student類和Classroom類

public class Student implements Cloneable {
    private String name;
    private Integer age;
    private Classroom classroom;

    public Student() {
        System.out.println("Student類的無參構造器...");
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("{");
        sb.append("\"Name\":\"")
                .append(name).append('\"');
        sb.append(",\"Age\":")
                .append(age);
        sb.append(",\"Classroom\":")
                .append(classroom);
        sb.append('}').append("    ").append(super.toString());
        return sb.toString();
    }

    //省略get、set方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.setClassroom((Classroom) student.classroom.clone());
        return student;
    }
}

public class Classroom implements Cloneable{

    private String className;

    public Classroom() {
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("{");
        sb.append("\"ClassName\":\"")
                .append(className).append('\"');
        sb.append('}').append(super.toString());
        return sb.toString();
    }

    //省略get、set
}

輸出結果:

Student類的無參構造器...
各個對象的地址
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@74a14482
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@1540e19d}    Prototype.BlogDemo.Student@677327b6
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@14ae5a5}    Prototype.BlogDemo.Student@7f31245a
修改student2和student3的屬性
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@1540e19d}    Prototype.BlogDemo.Student@677327b6
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@14ae5a5}    Prototype.BlogDemo.Student@7f31245a
Marry被調(diào)到高三<1>班
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c}    Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@1540e19d}    Prototype.BlogDemo.Student@677327b6
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@14ae5a5}    Prototype.BlogDemo.Student@7f31245a

由結果可以看出,使用強克隆可以為引用對象開辟一塊內(nèi)存地址而不是像淺克隆那樣指向同一個引用對象。

總結:

設計原型模式需要注意淺克隆和深克隆。

了解更多設計模式:

設計模式系列

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容