1. 程式人生 > >設計模式 | 原型模式(prototype)

設計模式 | 原型模式(prototype)

har 細節 info 克隆 proto trac ring 隱藏 內存

定義:

用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。

結構:(書中圖,侵刪)

技術分享圖片

一個申明克隆自己的接口 若幹具體的需要克隆自己的類 這個結構很簡單,而且在Java中那個接口是不需要自己寫的。 Java類庫中有現成的Cloneable接口,這只是一個標記接口,裏面沒有任何方法,但如果不加這個標記,會拋CloneNotSupportedException異常。 真正的clone()方法繼承自Object類,但是必須要重寫。 這裏的clone()方法是淺克隆。
這裏需要先解釋一下深克隆和淺克隆的區別: 克隆,顧名思義,就是復制一個一模一樣的。 java clone()方法的操作方式就是,開辟一塊和當前對象一樣的空間,然後把內容原樣拷貝過去。 這導致的結果就是: 基礎類型(char、int、long等)會是直接的值拷貝過去。 但是像對象這些拷貝過去的就只是引用,導致,所有克隆出來的孩子裏的引用類型都指向同一個地方,而不是每人新建了一份。 這就是淺克隆。 深克隆就是把上述指向同一個地方的引用換成了每個人都指向一個新的地方,裏面的值依舊是一樣的。(下面的例子會使用深克隆)
來舉個例子: 一個歌手,他有基本信息:姓名、年齡; 熱門歌曲信息:歌名、發行時間。 歌手類:
package designpattern.prototype;

public class Singer implements Cloneable {
    String name;
    int age;
    HotMusic hotMusic;

    public Singer(String name, int age, HotMusic hotMusic) {
        super();
        this.name = name;
        
this.age = age; this.hotMusic = hotMusic; } 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 HotMusic getHotMusic() { return hotMusic; } public void setHotMusic(HotMusic hotMusic) { this.hotMusic = hotMusic; } @Override protected Object clone() throws CloneNotSupportedException { Singer singerClone = (Singer) super.clone(); singerClone.hotMusic = (HotMusic) hotMusic.clone();// 這一句,下面解釋 return singerClone; } @Override public String toString() { return "Singer [name=" + name + ", age=" + age + ", hotMusic=" + hotMusic + "]"; } }
歌曲信息類:
package designpattern.prototype;

public class HotMusic implements Cloneable {
    String name;
    String date;

    public HotMusic(String name, String date) {
        super();
        this.name = name;
        this.date = date;
    }

    public String getName() {
        return name;
    }

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

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

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

    @Override
    public String toString() {
        return "HotMusic [name=" + name + ", date=" + date + "]";
    }

}
客戶端:
package designpattern.prototype;

public class Client {
    public static void main(String[] args) {
        Singer singer1 = new Singer("周傑倫", 40, new HotMusic("告白氣球", "2016"));
        System.out.println(singer1);

        try {
            Singer singer2 = (Singer) singer1.clone();
            singer2.hotMusic.setName("等你下課");
            singer2.hotMusic.setDate("2018");
            System.out.println("========================================================");
            System.out.println(singer1);
            System.out.println(singer2);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }

}
輸出結果:(深克隆)
Singer [name=周傑倫, age=40, hotMusic=HotMusic [name=告白氣球, date=2016]]
========================================================
Singer [name=周傑倫, age=40, hotMusic=HotMusic [name=告白氣球, date=2016]]
Singer [name=周傑倫, age=40, hotMusic=HotMusic [name=等你下課, date=2018]]
如果把歌手類中註釋的那一句去掉,結果會是:(淺克隆)
Singer [name=周傑倫, age=40, hotMusic=HotMusic [name=告白氣球, date=2016]]
========================================================
Singer [name=周傑倫, age=40, hotMusic=HotMusic [name=等你下課, date=2018]]
Singer [name=周傑倫, age=40, hotMusic=HotMusic [name=等你下課, date=2018]]

總結:

其實之前我從來都沒有用過這個方法,之前看到文中說,要創建多個一模一樣的對象,直接循環new不就得了?還搞那麽麻煩。 看到書中的話對我有很大的啟發,我就直接整理一下敲上來了: 每new一次,都要執行一次構造函數,如果構造函數的執行時間很長,那麽多次執行會很低效。 一般在初始化的信息不發生變化的情況下,克隆是最好的辦法。這既隱藏了對象的創建細節,又對性能是大大的提高。 結合我上面說的,克隆是直接復制內存的,顯然比執行構造方法高效的多。

設計模式 | 原型模式(prototype)