圖解Java設(shè)計模式之原型模式

@TOC

克隆羊的問題

現(xiàn)在有一只羊tom阔蛉,姓名為 : tom弃舒,年齡為 :1,顏色為 :白色状原,請編寫程序創(chuàng)建和tom羊?qū)傩酝耆嗤?0只羊聋呢。

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


    在這里插入圖片描述
package com.example.demo.prototype;

public class Sheep {
    private String name;
    private int age;
    private String color;
    
    public Sheep(String name, int 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 int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    @Override
    public String toString() {
        return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";
    }
    
}
package com.example.demo.prototype;

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("小樣", 12, "白色");
        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());
        System.out.println(sheep.toString());
        System.out.println(sheep2.toString());
        System.out.println(sheep3.toString());
        System.out.println(sheep4.toString());
    }
}

傳統(tǒng)的方式的優(yōu)缺點 :
1)優(yōu)點是比較好理解,簡單易操作颠区。
2)在創(chuàng)建新的對象時削锰,總是需要重新獲取原始對象的屬性,如果創(chuàng)建的對象比較復(fù)雜時毕莱,效率較低器贩。
3)總是需要重新初始化對象,而不是動態(tài)地獲得對象運(yùn)行時的狀態(tài)朋截,不夠靈活
思路 : 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è)計模式,運(yùn)行一個對象再創(chuàng)建另外一個可定制的對象胆描,無需知道如何創(chuàng)建的細(xì)節(jié)
3)工作原理是 :通過將一個原型對象傳給那個要發(fā)動創(chuàng)建的對象瘫想,這個要發(fā)動創(chuàng)建的對象通過請求原型對象拷貝它們自己來實現(xiàn)創(chuàng)建,即 對象.clone()
4)形象的理解 :孫大圣拔出猴毛昌讲,變出其它孫大圣


在這里插入圖片描述

原型結(jié)構(gòu)圖說明 :
1)Prototype : 原型類国夜,聲明一個克隆自己的接口
2)ConcretePrototype : 具體的原型類,實現(xiàn)一個克隆自己的操作
3)Client :讓一個原型對象克隆自己短绸,從而創(chuàng)建一個新的對象(屬性一樣)

package com.example.demo.prototype.improve;

public class Sheep implements Cloneable{
    private String name;
    private int age;
    private String color;
    
    public Sheep(String name, int 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 int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    
    /**
     * 克隆該實例车吹,使用默認(rèn)的clone方法來完成
     */
    @Override
    protected Sheep clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep)super.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sheep;
    }
    
    @Override
    public String toString() {
        return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";
    }
    
}
package com.example.demo.prototype.improve;

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("小樣", 12, "白色");
        Sheep sheep2 = (Sheep)sheep.clone();
        Sheep sheep3 = (Sheep)sheep.clone();
        Sheep sheep4 = (Sheep)sheep.clone();
        System.out.println(sheep.toString());
        System.out.println(sheep2.toString());
        System.out.println(sheep3.toString());
        System.out.println(sheep4.toString());
    }
}

原型模式在Spring框架中源碼分析

1)Spring中原型bean的創(chuàng)建筹裕,就是原型模式的應(yīng)用


在這里插入圖片描述

淺拷貝的介紹

1)對于數(shù)據(jù)類型是基本數(shù)據(jù)類的成員變量,淺拷貝會直接進(jìn)行值傳遞窄驹,也就是將該屬性值復(fù)制一份給新的對象朝卒。
2)對于數(shù)據(jù)類型是引用數(shù)據(jù)類型的成員變量,比如說成員變量是某個數(shù)組乐埠、某個類的對象等抗斤,那么淺拷貝會進(jìn)行引用傳遞,也就是只是將該成員變量的引用值(內(nèi)存地址)復(fù)制一份給新的對象丈咐。因為實際上兩對象的該成員變量都指向同一個實例瑞眼。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值
3)前面的克隆羊是淺拷貝
4)淺拷貝是使用默認(rèn)的clone()方法來實現(xiàn)
sheep = (Sheep)supper.clone();

深拷貝基本介紹

1)復(fù)制對象的所有基本數(shù)據(jù)類型的成員變量值
2)為所有引用數(shù)據(jù)類型的成員變量申請存儲空間棵逊,并復(fù)制每個引用數(shù)據(jù)類型成員變量所引用的對象伤疙,直到該對象可達(dá)的所有對象。也就是說歹河,對象進(jìn)行深拷貝要對整個對象進(jìn)行拷貝
3)深拷貝實現(xiàn)方式1 :重寫clone方法來實現(xiàn)深拷貝
4)深拷貝實現(xiàn)方式2 :通過對象序列化實現(xiàn)深拷貝

package com.example.demo.prototype.deepclone;

import java.io.Serializable;

public class DeepCloneableTarget implements Serializable, Cloneable {

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

    public DeepCloneableTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return super.clone();
    }

}
package com.example.demo.prototype.deepclone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DeepProtoType implements Serializable, Cloneable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    /**
     * 屬性
     */
    public String name;
    
    /**
     * 引用類型
     */
    public DeepCloneableTarget deepCloneableTarget;
    
    public DeepProtoType() {
        
    }
    
    /**
     * 深拷貝 - 方式1 使用clone 方法
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deepObject = null;
        // 這里完成對基本數(shù)據(jù)類型(屬性)和String的克隆
        deepObject = super.clone();
        // 對引用類型的屬性掩浙,進(jìn)行單獨處理
        DeepProtoType deepProtoType = (DeepProtoType)deepObject;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone(); 
        return deepProtoType;
    }
    
    /**
     * 深拷貝 - 方式2 通過對象的序列化實現(xiàn)(推薦)
     * 
     * @return
     */
    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);
            // 當(dāng)前這個對象以對象流的方式輸出
            oos.writeObject(this);
            
            // 反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType copyObjecType = (DeepProtoType)ois.readObject();
            return copyObjecType;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            // 關(guān)閉流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
    }

}
package com.example.demo.prototype.deepclone;

public class Client {
    public static void main(String[] args) throws Exception {
        DeepProtoType deepProtoType = new DeepProtoType();
        deepProtoType.name = "松江";
        deepProtoType.deepCloneableTarget = new DeepCloneableTarget("大牛", "大牛的類");
        
        // 方式1 完成深拷貝
        //DeepProtoType deepProtoType2 = (DeepProtoType)deepProtoType.clone();
        
        //System.out.println("deepProtoType.name = " + deepProtoType.name + "deepProtoType.deepCloneableTarget = " + deepProtoType.deepCloneableTarget.hashCode());
        //System.out.println("deepProtoType.name = " + deepProtoType2.name + "deepProtoType.deepCloneableTarget = " + deepProtoType2.deepCloneableTarget.hashCode());
        
        DeepProtoType deepProtoType2 = (DeepProtoType)deepProtoType.deepClone();
        
        System.out.println("deepProtoType.name = " + deepProtoType.name + "deepProtoType.deepCloneableTarget = " + deepProtoType.deepCloneableTarget.hashCode());
        System.out.println("deepProtoType.name = " + deepProtoType2.name + "deepProtoType.deepCloneableTarget = " + deepProtoType2.deepCloneableTarget.hashCode());
    }
}

  • 原型模型的注意事項和細(xì)節(jié)
    1)創(chuàng)建新的對象比較復(fù)雜時,可以利用原型模式簡化對象的創(chuàng)建過程秸歧,同時也能夠提高效率
    2)不用重新初始化對象厨姚,而是動態(tài)地獲得對象運(yùn)行時的狀態(tài)
    3)如果原始對象發(fā)生變化(增加或者減少屬性),其它克隆對象的也會發(fā)生相應(yīng)的編號键菱,無需修改代碼
    4)在實現(xiàn)深克隆的時候可能需要比較復(fù)雜的代碼
    5)缺點 : 需要為每一個類裝配一個克隆方法谬墙,這對全新的類來說不是很難,但對已有的類進(jìn)行改造時经备,需要修改其源代碼拭抬,違背了ocp原則,這點需要注意侵蒙。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末造虎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纷闺,更是在濱河造成了極大的恐慌算凿,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犁功,死亡現(xiàn)場離奇詭異氓轰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)浸卦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門署鸡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事靴庆∈卑疲” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵撒穷,是天一觀的道長匣椰。 經(jīng)常有香客問我,道長端礼,這世上最難降的妖魔是什么禽笑? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蛤奥,結(jié)果婚禮上佳镜,老公的妹妹穿的比我還像新娘。我一直安慰自己凡桥,他們只是感情好蟀伸,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缅刽,像睡著了一般啊掏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衰猛,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天迟蜜,我揣著相機(jī)與錄音,去河邊找鬼啡省。 笑死娜睛,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卦睹。 我是一名探鬼主播畦戒,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼结序!你這毒婦竟也來了障斋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤徐鹤,失蹤者是張志新(化名)和其女友劉穎配喳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凳干,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年被济,在試婚紗的時候發(fā)現(xiàn)自己被綠了救赐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖经磅,靈堂內(nèi)的尸體忽然破棺而出泌绣,到底是詐尸還是另有隱情,我是刑警寧澤预厌,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布阿迈,位于F島的核電站,受9級特大地震影響轧叽,放射性物質(zhì)發(fā)生泄漏苗沧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一炭晒、第九天 我趴在偏房一處隱蔽的房頂上張望待逞。 院中可真熱鬧,春花似錦网严、人聲如沸识樱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怜庸。三九已至,卻和暖如春垢村,著一層夾襖步出監(jiān)牢的瞬間割疾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工肝断, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留杈曲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓胸懈,卻偏偏與公主長得像担扑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子趣钱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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