設(shè)計模式——原型設(shè)計模式

克隆羊問題

現(xiàn)在有一只羊 tom,姓名為: tom, 年齡為:1壕探,顏色為:白色逆甜,請編寫程序創(chuàng)建和 tom 羊 屬性完全相同的 10只羊。

1. 傳統(tǒng)方式解決克隆羊問題

        Sheep sheep = new Sheep("tom", 1, "白色");

        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep6 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep7 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep8 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep9 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep10 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());

傳統(tǒng)的方式的優(yōu)缺點

  1. 優(yōu)點是比較好理解距帅,簡單易操作。

  2. 在創(chuàng)建新的對象時括堤,總是需要重新獲取原始對象的屬性碌秸,如果創(chuàng)建的對象比較復(fù)雜時,效率較低

  3. 總是需要重新初始化對象悄窃,而不是動態(tài)地獲得對象運行時的狀態(tài), 不夠靈活

  4. 改進的思路分析

思路:Java 中 Object 類是所有類的根類讥电,Object 類提供了一個 clone()方法,該方法可以將一個 Java 對象復(fù)制一份轧抗,但是需要實現(xiàn) clone 的 Java 類必須要實現(xiàn)一個接口 Cloneable恩敌,該接口表示該類能夠復(fù)制且具有復(fù)制的能力 =>原型模式

原型模式-基本介紹

基本介紹

  1. 原型模式(Prototype 模式)是指:用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型横媚,創(chuàng)建新的對象
  2. 原型模式是一種創(chuàng)建型設(shè)計模式纠炮,允許一個對象再創(chuàng)建另外一個可定制的對象月趟,無需知道如何創(chuàng)建的細節(jié)
  3. 工作原理是:通過將一個原型對象傳給那個要發(fā)動創(chuàng)建的對象,這個要發(fā)動創(chuàng)建的對象通過請求原型對象拷貝它們自己來實施創(chuàng)建恢口,即 對象.clone()

2.原型模式的使用

代碼展示

public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;

    public Sheep(String name, Integer age, String color) {
        super();
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return sheep;
    }

}
public class Client {
    public static void main(String[] args) {
        //原型模式做法
        Sheep sheep = new Sheep("tom", 1, "白色");

         Sheep sheep2 = (Sheep)sheep.clone();
         Sheep sheep3 = (Sheep)sheep.clone();
         Sheep sheep4 = (Sheep)sheep.clone();
         Sheep sheep5 = (Sheep)sheep.clone();

        System.out.println(sheep);
        System.out.println(sheep2);
        System.out.println(sheep3);
        System.out.println(sheep4);
        System.out.println(sheep5);
    }
}
image.png

3.Spring中的使用

image.png
image.png

4.淺拷貝和深拷貝

4.1 淺拷貝的介紹

  1. 對于數(shù)據(jù)類型是基本數(shù)據(jù)類型的成員變量孝宗,淺拷貝會直接進行值傳遞,也就是將該屬性值復(fù)制一份給新的對象耕肩。
  2. 對于數(shù)據(jù)類型是引用數(shù)據(jù)類型的成員變量因妇,比如說成員變量是某個數(shù)組、某個類的對象等看疗,那么淺拷貝會進行引用傳遞沙峻,也就是只是將該成員變量的引用值(內(nèi)存地址)復(fù)制一份給新的對象。因為實際上兩個對象的該成員變量都指向同一個實例两芳。在這種情況下摔寨,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值
  3. 前面我們克隆羊就是淺拷貝
  4. 淺拷貝是使用默認(rèn)的 clone()方法來實現(xiàn)
    sheep = (Sheep) super.clone();
  • 基本數(shù)據(jù)類型
    基本數(shù)據(jù)類型只有8種,可按照如下分類
    ①整數(shù)類型:long怖辆、int是复、short、byte
    ②浮點類型:float竖螃、double
    ③字符類型:char
    ④布爾類型:boolean
  • 引用數(shù)據(jù)類型
    引用數(shù)據(jù)類型非常多淑廊,大致包括:
    類、 接口類型特咆、 數(shù)組類型季惩、 枚舉類型、 注解類型腻格、 字符串型
    例如画拾,String類型就是引用類型。
image.png
public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;
    public Sheep friend;
    
    public Sheep(String name, Integer age, String color) {
        super();
        this.name = name;
        this.age = age;
        this.color = color;
    }
}

main中

        Sheep sheep = new Sheep("tom", 1, "白色");
        sheep.friend = new Sheep("jack", 2, "黑色");

         Sheep sheep2 = (Sheep)sheep.clone();
         Sheep sheep3 = (Sheep)sheep.clone();
         Sheep sheep4 = (Sheep)sheep.clone();
         Sheep sheep5 = (Sheep)sheep.clone();

        System.out.println(sheep+ " : "+ sheep.friend.hashCode());
        System.out.println(sheep2+ " : "+ sheep2.friend.hashCode());
        System.out.println(sheep3+ " : "+ sheep3.friend.hashCode());
        System.out.println(sheep4+ " : "+ sheep4.friend.hashCode());
        System.out.println(sheep5+ " : "+ sheep.friend.hashCode());
image.png

疑問:

//原型模式做法
        Sheep sheep = new Sheep("tom", 1, "白色");

        sheep.friend = new Sheep("jack", 2, "黑色");

         Sheep sheep2 = (Sheep)sheep.clone();
         Sheep sheep3 = (Sheep)sheep.clone();
         Sheep sheep4 = (Sheep)sheep.clone();
         Sheep sheep5 = (Sheep)sheep.clone();

        System.out.println(sheep.hashCode()+ " : "+ sheep.friend.hashCode());
        System.out.println(sheep2.hashCode()+ " : "+ sheep2.friend.hashCode());
        System.out.println(sheep3.hashCode()+ " : "+ sheep3.friend.hashCode());
        System.out.println(sheep4.hashCode()+ " : "+ sheep4.friend.hashCode());
        System.out.println(sheep5.hashCode()+ " : "+ sheep.friend.hashCode());
image.png

4.2 深拷貝基本介紹

  1. 復(fù)制對象的所有基本數(shù)據(jù)類型的成員變量值

  2. 為所有引用數(shù)據(jù)類型的成員變量申請存儲空間菜职,并復(fù)制每個引用數(shù)據(jù)類型成員變量所引用的對象青抛,直到該對象可達的所有對象。也就是說酬核,對象進行深拷貝要對整個對象(包括對象的引用類型)進行拷貝

  3. 深拷貝實現(xiàn)方式 1:重寫 clone 方法來實現(xiàn)深拷貝

  4. 深拷貝實現(xiàn)方式 2:通過對象序列化實現(xiàn)深拷貝(推薦)

public class DeepCloneableTarget implements Serializable, Cloneable {

    private static final long serialVersionUID = 1L;
    private String cloneName;
    private String cloneClass;

    //構(gòu)造器
    public DeepCloneableTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

    //因為該類的屬性蜜另,都是 String ,  因此我們這里使用默認(rèn)的 clone 完成即可
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
package com.cn.bean;

import java.io.*;

public class DeepProtoType implements Serializable, Cloneable {
    public String name; //String 屬 性
    public DeepCloneableTarget deepCloneableTarget;// 引用類型

    public DeepProtoType() {
        super();
    }

    //深拷貝 - 方式 1  使用 clone 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //這里完成對基本數(shù)據(jù)類型(屬性)和 String 的克隆
        deep = super.clone();
        //對引用類型的屬性,進行單獨處理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
        return deepProtoType;
    }

    ///深拷貝 - 方式 2 通過對象的序列化實現(xiàn) (推薦)
    public Object deepClone() {
        //創(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); //當(dāng)前這個對象以對象流的方式輸出
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType copyObj = (DeepProtoType) ois.readObject();
            return copyObj;
        } catch (Exception e) {
            return null;
        } finally {
            //關(guān)閉流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2);
            }
        }

    }
}

package com.cn.bean;

public class Client {
    public static void main(String[] args) throws Exception {

        DeepProtoType p = new DeepProtoType();
        p.name = "宋江";
        p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");

        //方式 1  完成深拷貝
        DeepProtoType p2 = (DeepProtoType) p.clone();
        System.out.println("使用 clone 方法");
        System.out.println("p.name=" + p.name + "   p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
        System.out.println("p2.name=" + p2.name + "   p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());

       // 方式 2  完成深拷貝
        DeepProtoType p3 = (DeepProtoType) p.deepClone();
        System.out.println("通過對象的序列化實現(xiàn)");
        System.out.println("p.name=" + p.name + "  p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
        System.out.println("p3.name=" + p3.name + "  p2.deepCloneableTarget=" + p3.deepCloneableTarget.hashCode());

    }
}
image.png

5. 原型模式的注意事項和細節(jié)

  1. 創(chuàng)建新的對象比較復(fù)雜時嫡意,可以利用原型模式簡化對象的創(chuàng)建過程举瑰,同時也能夠提高效率
  2. 不用重新初始化對象,而是動態(tài)地獲得對象運行時的狀態(tài)
  3. 如果原始對象發(fā)生變化(增加或者減少屬性)蔬螟,其它克隆對象的也會發(fā)生相應(yīng)的變化嘶居,無需修改代碼
  4. 在實現(xiàn)深克隆的時候可能需要比較復(fù)雜的代碼
  5. 缺點:需要為每一個類配備一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時邮屁,需要修改其源代碼,違背了 ocp 原則菠齿,這點請同學(xué)們注意.

6. 應(yīng)用場景

原型模式通常適用于以下場景佑吝。

  • 對象之間相同或相似,即只是個別的幾個屬性不同的時候绳匀。
  • 創(chuàng)建對象成本較大芋忿,例如初始化時間長,占用CPU太多疾棵,或者占用網(wǎng)絡(luò)資源太多等戈钢,需要優(yōu)化資源。
  • 創(chuàng)建一個對象需要繁瑣的數(shù)據(jù)準(zhǔn)備或訪問權(quán)限等是尔,需要提高性能或者提高安全性殉了。
  • 系統(tǒng)中大量使用該類對象,且各個調(diào)用者都需要給它的屬性重新賦值拟枚。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末薪铜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子恩溅,更是在濱河造成了極大的恐慌隔箍,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脚乡,死亡現(xiàn)場離奇詭異蜒滩,居然都是意外死亡,警方通過查閱死者的電腦和手機奶稠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門俯艰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窒典,你說我怎么就攤上這事蟆炊。” “怎么了瀑志?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵涩搓,是天一觀的道長。 經(jīng)常有香客問我劈猪,道長昧甘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任战得,我火速辦了婚禮充边,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己浇冰,他們只是感情好贬媒,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肘习,像睡著了一般际乘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漂佩,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天脖含,我揣著相機與錄音,去河邊找鬼投蝉。 笑死养葵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瘩缆。 我是一名探鬼主播关拒,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼咳榜!你這毒婦竟也來了夏醉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤涌韩,失蹤者是張志新(化名)和其女友劉穎畔柔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體臣樱,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡靶擦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雇毫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玄捕。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖棚放,靈堂內(nèi)的尸體忽然破棺而出枚粘,到底是詐尸還是另有隱情,我是刑警寧澤飘蚯,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布馍迄,位于F島的核電站,受9級特大地震影響局骤,放射性物質(zhì)發(fā)生泄漏攀圈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一峦甩、第九天 我趴在偏房一處隱蔽的房頂上張望赘来。 院中可真熱鬧,春花似錦、人聲如沸犬辰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幌缝。三九已至默色,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狮腿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工呕诉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缘厢,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓甩挫,卻偏偏與公主長得像贴硫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子伊者,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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