三、原型模式

定義

當創建給定實例的過程很複雜時,就應該使用原型模式(Prototype)。

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

原型模式屬於創建型模式。

 

要點

原型模式允許通過複製現有的實例來創建新的實例,在Java中,這通常意味着使用 clone() 方法或反序列化。

  • 向客戶隱藏製造新實例的複雜性。
  • 提供讓客戶能夠產生未知類型對象的選項。
  • 在某些環境下,複製對象比創建對象更有效。

 
在這裏插入圖片描述

  1. Prototype: 原型類,聲明一個克隆自己的接口
  2. ConcretePrototype: 具體的原型類, 實現一個克隆自己的操作
  3. Client: 讓一個原型對象克隆自己,從而創建一個新的對象

 

場景

爲了快速構造一個和已有對象相同的副本。 在一個複雜的類層次中,當系統必須從其中的許多類型創建新對象時,可以考慮原型。

例如,克隆羊問題,現在有一隻羊實例,請創建和該羊屬性完全相同的 3 只羊。

public class Client {

	public static void main(String[] args) {
		// 傳統的方法
		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());
	}
	
}

這種傳統的方式簡單易操作,但是:

  1. 創建新對象時,需要重新獲取原始對象的屬性,若原始對象相對複雜時,效率較低。
  2. 需要重新初始化對象,而不是動態地獲得對象運行時的狀態, 不夠靈活。

 

實現

/**
 * 羊,實現了Cloneable接口
 */
public class Sheep implements Cloneable {

    /**
     * 羊的名字
     */
    private String name;
    /**
     * 羊的年齡
     */
    private int age;
    /**
     * 羊的顏色
     */
    private String color;

    /**
     * 該羊的母親 (clone默認是淺拷貝)
     */
    private Sheep mother;

    public Sheep(String name, int age, String color, Sheep mother) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.mother = mother;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getColor() {
        return color;
    }

    public Sheep getMother() {
        return mother;
    }

    @Override
    public String toString() {
        return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", mother's hashcode = " + mother.hashCode() + "]";
    }

   /**
     * 克隆該實例,使用默認的 clone 方法來完成
     */
    @Override
    protected Object clone() {
        Sheep sheep = null;

        try {
            sheep = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return sheep;
    }

}

Client

/**
 * 使用原型模式創建新對象
 */
public class Client {

    public static void main(String[] args) {
        System.out.println("原型模式完成新對象的創建 --> \n");

        // 原始羊(被克隆的羊)
        Sheep sheep = new Sheep("羊羔", 1, "白色", new Sheep("母親", 3, "黑白色", null));

        // 克隆羊
        Sheep cloneSheep1 = (Sheep) sheep.clone();
        Sheep cloneSheep2 = (Sheep) sheep.clone();

        // 原始羊信息
        System.out.println("原始羊  : " + sheep);
        // 克隆羊信息
        System.out.println("克隆羊1 : " + cloneSheep1);
        System.out.println("克隆羊2 : " + cloneSheep2);
    }

}

源代碼

Click here

 

總結

  1. 關於原型模式
    創建新的對象比較複雜時,可以利用原型模式簡化對象的創建過程,同時也能夠提高效率
    不用重新初始化對象,而是動態地獲得對象運行時的狀態
    如果原始對象發生變化(增加或者減少屬性),其它克隆對象的也會發生相應的變化,無需修改代碼
    在實現深克隆的時候可能需要比較複雜的代碼

缺點:需要爲每一個類配備一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時,需要修改其源代碼,違背了 ocp 原則,這點請注意.

  1. clone 默認使用的是淺拷貝(shadow clone)
    若實例域是基本數據類型,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的對象。
    若實例域是引用數據類型,例如某個數組、某個類對象等,那麼淺拷貝只會進行引用傳遞,只是將該成員變量的引用值(內存地址)複製一份給新的對象,實際上使用的是相同的成員變量對象。

應用:Spring 中原型 bean 的創建,就是原型模式的應用

 

實現深拷貝的方法(deep clone)

  • 使用對象序列化來實現深拷貝(推薦)
  • 重寫 clone 方法實現深拷貝

序列化實現

傳入的對象需要實現 Serializable 接口

/**
 * 深拷貝一個對象
 */
public Object deepClone(Object src) {
    Object object = null;

    try {
        if (src != null) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(src);
            oos.close();
            
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            object = ois.readObject();
            ois.close();
        }
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }

    return object;
}

重寫clone實現

@Override
protected Object clone() {
    Sheep sheep = null;

    try {
        sheep = (Sheep) super.clone();
        sheep.mother = this.mother == null ? null : (Sheep) this.mother.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }

    return sheep;
}
發佈了21 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章