設(shè)計模式學(xué)習(xí)05(Java實現(xiàn))——原型模式

寫在前面

  • 記錄學(xué)習(xí)設(shè)計模式的筆記
  • 提高對設(shè)計模式的靈活運用

學(xué)習(xí)地址

https://www.bilibili.com/video/BV1G4411c7N4

https://www.bilibili.com/video/BV1Np4y1z7BU

參考文章

http://c.biancheng.net/view/1317.html

項目源碼
https://gitee.com/zhuang-kang/DesignPattern

6闹伪,原型模式

6.1原形模式的定義和特點

原型(Prototype)模式的定義如下:用一個已經(jīng)創(chuàng)建的實例作為原型夺颤,通過復(fù)制該原型對象來創(chuàng)建一個和原型相同或相似的新對象梯浪。在這里胰耗,原型實例指定了要創(chuàng)建的對象的種類。用這種方式創(chuàng)建對象非常高效五督,根本無須知道對象創(chuàng)建的細(xì)節(jié)绵载。例如,Windows 操作系統(tǒng)的安裝通常較耗時乘盖,如果復(fù)制就快了很多焰檩。

原型模式的優(yōu)點:

  • Java自帶的原型模式基于內(nèi)存二進(jìn)制流的復(fù)制憔涉,在性能上比直接 new 一個對象更加優(yōu)良。
  • 可以使用深克隆方式保存對象的狀態(tài)析苫,使用原型模式將對象復(fù)制一份兜叨,并將其狀態(tài)保存起來,簡化了創(chuàng)建對象的過程衩侥,以便在需要的時候使用(例如恢復(fù)到歷史某一狀態(tài))国旷,可輔助實現(xiàn)撤銷操作。

原型模式的缺點:

  • 需要為每一個類都配置一個 clone 方法
  • clone 方法位于類的內(nèi)部茫死,當(dāng)對已有類進(jìn)行改造的時候跪但,需要修改代碼,違背了開閉原則峦萎。
  • 當(dāng)實現(xiàn)深克隆時屡久,需要編寫較為復(fù)雜的代碼,而且當(dāng)對象之間存在多重嵌套引用時爱榔,為了實現(xiàn)深克隆被环,每一層對象對應(yīng)的類都必須支持深克隆,實現(xiàn)起來會比較麻煩详幽。因此筛欢,深克隆、淺克隆需要運用得當(dāng)唇聘。

6.2 原型模式的結(jié)構(gòu)與實現(xiàn)

6.2.1 原形模式的結(jié)構(gòu)

原型模式包含以下主要角色版姑。

  1. 抽象原型類:規(guī)定了具體原型對象必須實現(xiàn)的接口。
  2. 具體原型類:實現(xiàn)抽象原型類的 clone() 方法迟郎,它是可被復(fù)制的對象漠酿。
  3. 訪問類:使用具體原型類中的 clone() 方法來復(fù)制新的對象。

6.2.2 代碼實現(xiàn)

淺克禄涯丁:創(chuàng)建一個新對象炒嘲,新對象的屬性和原來對象完全相同宇姚,對于非基本類型屬性,仍指向原有屬性所指向的對象的內(nèi)存地址夫凸。

深克禄肜汀:創(chuàng)建一個新對象,屬性中引用的其他對象也會被克隆夭拌,不再指向原有對象地址魔熏。

6.2.2.1 淺拷貝

IdCard

package com.zhuang.prototype.shallowclone;

/**
 * @Classname IdCard
 * @Description 淺拷貝的示例
 * @Date 2021/3/19 12:16
 * @Created by dell
 */

public class IdCard {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "id=" + id +
                '}';
    }
}

Person

package com.zhuang.prototype.shallowclone;

/**
 * @Classname Person
 * @Description 淺拷貝的示例
 * @Date 2021/3/19 12:17
 * @Created by dell
 */

public class Person implements Cloneable {

    private String name;
    private int age;
    private IdCard idCard;

    public Person() {
    }

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    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 IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", idCard=" + idCard + ", idCard.hashCode=" + idCard.hashCode() +
                '}';
    }

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

PersonTest

package com.zhuang.prototype.shallowclone;

/**
 * @Classname PersonTest
 * @Description 淺拷貝測試類
 * @Date 2021/3/19 12:17
 * @Created by dell
 */

public class PersonTest {
    public static void main(String[] args) throws Exception {
        Person person = new Person("張三", 20, new IdCard("10086"));

        Person person1 = (Person) person.clone();
        Person person2 = (Person) person.clone();

        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);

    }
}
image

我們發(fā)現(xiàn)可以通過實現(xiàn)implements Cloneable來完成淺拷貝,基本變量是值傳遞克隆鸽扁,而引用對象IdCard則是引用傳遞蒜绽,這不符合我們面向?qū)ο笏枷耄恳粋€Person應(yīng)該都有一個獨立的IdCard桶现,而不是共用一個躲雅,而要解決這種問題,我們需要使用深克隆

6.2.2.2 深拷貝(第一種)

IdCard

package com.zhuang.prototype.deepclone.one;

/**
 * @Classname IdCard
 * @Description 深克隆的示例
 * @Date 2021/3/19 12:33
 * @Created by dell
 */
//實現(xiàn)Cloneable接口
public class IdCard implements Cloneable {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "id='" + id + '\'' +
                '}';
    }

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

Person

package com.zhuang.prototype.deepclone.one;

/**
 * @Classname Person
 * @Description 深克隆的示例
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class Person implements Cloneable {
    private String name;
    private int age;
    private IdCard idCard;

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Person{" +
                "personHashCode=" + this.hashCode() +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", idCard=" + idCard +
                ", idCardHashCode=" + idCard.hashCode() +
                '}';
    }

    //深克隆需要自己手動實現(xiàn)骡和,在對象引用中也要實現(xiàn)clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //完成基本數(shù)據(jù)類型的拷貝
        //通過new關(guān)鍵字創(chuàng)建的對象是引用類型

        Object person = super.clone();

        //對引用類型單獨處理
        Person p = (Person) person;
        IdCard idCard = (IdCard) p.getIdCard().clone(); //實現(xiàn)自己的克隆
        p.setIdCard(idCard);
        return p;
    }
}

PersonTest

package com.zhuang.prototype.deepclone.one;

import java.io.Serializable;

/**
 * @Classname PersonTest
 * @Description 深克隆測試類
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class PersonTest implements Serializable {
    public static void main(String[] args) throws Exception {

        Person person = new Person("張三", 20, new IdCard("10086"));

        Person person1 = (Person) person.clone();
        Person person2 = (Person) person.clone();

        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);

    }
}
image

使用這種深克隆的方式相赁,完美的解決了當(dāng)數(shù)據(jù)類型為引用類型時,只是拷貝原引用對象地址而不是一個全新的引用對象的引用慰于,但是這種實現(xiàn)有一個很大的弊端钮科,需要在每一個對象中都實現(xiàn)clone方法,如果類全是你自己寫的婆赠,那自然沒問題绵脯,實現(xiàn)一下就行了,不過有點麻煩休里。但是蛆挫,如果你引用的是第三方的一個類,無法修改源代碼份帐,這種方式璃吧,顯然就無法實現(xiàn)深克隆了

6.2.2.2 深拷貝(第二種)

IdCard

package com.zhuang.prototype.deepclone.two;

/**
 * @Classname IdCard
 * @Description 深克隆的示例2
 * @Date 2021/3/19 12:33
 * @Created by dell
 */
//實現(xiàn)Serializable接口
public class IdCard implements Serializable {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "id='" + id + '\'' +
                '}';
    }

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

Person

package com.zhuang.prototype.deepclone.two;

import java.io.*;

/**
 * @Classname Person
 * @Description 深克隆的示例2
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class Person implements Serializable {
    private String name;
    private int age;
    private IdCard idCard;

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Person{" +
                "personHashCode=" + this.hashCode() +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", idCard=" + idCard +
                ", idCardHashCode=" + idCard.hashCode() +
                '}';
    }

    //序列化的方式
    public Person deelClone() {
        //創(chuàng)建流對象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            return (Person) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                ois.close();
                bis.close();
                oos.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

PersonTest

package com.zhuang.prototype.deepclone.two;

/**
 * @Classname PersonTest
 * @Description 深克隆測試類
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class PersonTest {
    public static void main(String[] args) throws Exception {
        //創(chuàng)建一個對象
        Person person = new Person("張三", 20, new IdCard("10086"));

        //克隆兩個對象
        Person person1 = (Person) person.deelClone();
        Person person2 = (Person) person.deelClone();
        System.out.println("深拷貝(第二種 實現(xiàn)序列化接口)");
        //打印三人信息
        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);

    }
}
image

這種方式我們需要手動編寫deepClone方法,使用Java流中的序列化與反序列化來實現(xiàn)深克隆废境,但是這種實現(xiàn)畜挨,需要在每一個類中都繼承序列化Serializable接口,這種方式噩凹,如果你調(diào)用的是第三方類巴元,也有可能第三方類上沒有實現(xiàn)Serializable序列化接口,但是一般來說驮宴,大多都會實現(xiàn)逮刨,總的來說,這種比較推薦使用,而且效率也高

6.3 原型模式的應(yīng)用場景

  • 對象之間相同或相似修己,即只是個別的幾個屬性不同的時候恢总。
  • 創(chuàng)建對象成本較大,例如初始化時間長睬愤,占用CPU太多片仿,或者占用網(wǎng)絡(luò)資源太多等,需要優(yōu)化資源尤辱。
  • 創(chuàng)建一個對象需要繁瑣的數(shù)據(jù)準(zhǔn)備或訪問權(quán)限等砂豌,需要提高性能或者提高安全性。
  • 系統(tǒng)中大量使用該類對象光督,且各個調(diào)用者都需要給它的屬性重新賦值阳距。
  • 在 Spring中,原型模式應(yīng)用的非常廣泛结借,例如 scope='prototype'筐摘、JSON.parseObject() 等都是原型模式的具體應(yīng)用。

6.4 原型模式的注意事項和細(xì)節(jié)

  • 創(chuàng)建新的對象比較復(fù)雜時映跟,可以利用原型模式簡化對象的創(chuàng)建過程蓄拣,同時也能提高效率
  • 不用重新初始化對象扬虚,動態(tài)地獲得對象運行時的狀態(tài)
  • 如果原始對象發(fā)生變化(增加或減少屬性)努隙,其他克隆對象也會發(fā)生相應(yīng)的變化,無需修改代碼

寫在最后

  • 如果我的文章對你有用辜昵,請給我點個??荸镊,感謝你??!
  • 有問題堪置,歡迎在評論區(qū)指出躬存!??
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市舀锨,隨后出現(xiàn)的幾起案子岭洲,更是在濱河造成了極大的恐慌,老刑警劉巖坎匿,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盾剩,死亡現(xiàn)場離奇詭異,居然都是意外死亡替蔬,警方通過查閱死者的電腦和手機告私,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來承桥,“玉大人驻粟,你說我怎么就攤上這事⌒滓欤” “怎么了蜀撑?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵挤巡,是天一觀的道長。 經(jīng)常有香客問我酷麦,道長玄柏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任贴铜,我火速辦了婚禮粪摘,結(jié)果婚禮上屹徘,老公的妹妹穿的比我還像新娘掘殴。我一直安慰自己,他們只是感情好净嘀,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布轩褐。 她就那樣靜靜地躺著椎咧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪把介。 梳的紋絲不亂的頭發(fā)上勤讽,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音拗踢,去河邊找鬼脚牍。 笑死,一個胖子當(dāng)著我的面吹牛巢墅,可吹牛的內(nèi)容都是我干的诸狭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼君纫,長吁一口氣:“原來是場噩夢啊……” “哼驯遇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蓄髓,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤叉庐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后会喝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陡叠,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年好乐,在試婚紗的時候發(fā)現(xiàn)自己被綠了匾竿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡蔚万,死狀恐怖岭妖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤昵慌,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布假夺,位于F島的核電站,受9級特大地震影響斋攀,放射性物質(zhì)發(fā)生泄漏已卷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一淳蔼、第九天 我趴在偏房一處隱蔽的房頂上張望侧蘸。 院中可真熱鬧,春花似錦鹉梨、人聲如沸讳癌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晌坤。三九已至,卻和暖如春旦袋,著一層夾襖步出監(jiān)牢的瞬間骤菠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工疤孕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留商乎,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓胰柑,卻偏偏與公主長得像截亦,于是被迫代替她去往敵國和親爬泥。 傳聞我的和親對象是個殘疾皇子柬讨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容