1. 程式人生 > 程式設計 >23種設計模式之原型模式

23種設計模式之原型模式

1、定義

用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。

2、原型模式兩種表現形式

2.1 簡單形式

2.1.1 模式結構

原型模式簡單形式由三部分組成:

  • 客戶(Client)角色:客戶類提出建立物件的請求。
  • 抽象原型(Prototype)角色:這是一個抽象角色,宣告一個克隆自己的介面。
  • 具體原型(Concrete Prototype)角色:被複制的物件,需要實現抽象的原型角色所要求的介面。

2.1.2 例項

2.1.2.1 抽象原型

public abstract class Prototype implements Cloneable {
 
    @Override
    public Object clone
() throws CloneNotSupportedException { return super.clone(); } } 複製程式碼

2.1.2.2 具體原型

public class ConcretePrototype extends Prototype {
    // 其他操作
}
複製程式碼

2.1.2.3 客戶端呼叫

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype prototype = new ConcretePrototype();
        ConcretePrototype concretePrototype = (ConcretePrototype)prototype.clone();
       
        System.out.println("prototype.hashCode="
+ prototype.hashCode()); System.out.println("concretePrototype.hashCode=" + concretePrototype.hashCode()); } } 複製程式碼
2.2 登記形式

2.2.1 模式結構

原型模式登記形式由四部分組成:

  • 客戶(Client)角色:客戶類提出建立物件的請求。
  • 抽象原型(Prototype)角色:這是一個抽象角色,宣告一個克隆自己的介面。
  • 具體原型(Concrete Prototype)角色:被複制的物件,需要實現抽象的原型角色所要求的介面。
  • 原型管理器(Prototype Manager)角色:提供各種原型物件的建立和管理。

2.2.2 例項

2.2.2.1 抽象原型

public abstract class Prototype implements Cloneable {

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
複製程式碼

2.2.2.2 具體原型

public class ConcretePrototype extends Prototype {
    // 其他操作
}
複製程式碼

2.2.2.3 原型管理器

public class PrototypeManager {

    private static Map<String,Prototype> map = new HashMap<>();
    
    private PrototypeManager(){
    }
    
    public static void setPrototype(String name,prototype prototype) {
        map.put(name,prototype);
    }
    
    public static void removePrototype(String name) {
        map.remove(name);
    }
    
    public static Prototype getPrototype(String name) throws Exception {
        Prototype prototype = map.get(name);
        
        if (prototype == null) {
            throw new Exception("找不到原型");
        }
        return prototype;
    }
}
複製程式碼

2.2.2.4 客戶端呼叫

public class Test {
    public static void main(String[] args) {
        try {
            Prototype prototype = new ConcretePrototype();
            PrototypeManager.setPrototype("com.freedom.ConcretePrototype",prototype);

            ConcretePrototype concretePrototype = (ConcretePrototype)PrototypeManager.getPrototype("com.freedom.ConcretePrototype").clone();

            System.out.println("prototype.hashcode=" + prototype.hashCode());
            System.out.println("concretePrototype.hashcode=" + concretePrototype.hashCode());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
複製程式碼
2.3 兩種形式的比較
  • 如果要建立的原型物件資料較少而且比較固定的話,可採用第一種形式。在這種情況下,原型物件的引用可以由客戶端自己儲存。
  • 如果要建立的原型物件資料不固定的話,可以採用第二種形式。在這種情況下,客戶端不儲存對原型物件的引用,這個任務被交給原型管理器角色。在克隆一個物件之前,客戶端可以檢視管理員物件是否已用一個滿足要求的原型物件。如果有,可以從原型管理器角色取得這個物件引用;然後沒有,客戶端就需要自行復制此原型物件。

3、優缺點

3.1 優點
  • 建立新的物件比較複雜時,可以利用原型原型模式簡化物件的建立過程,同時也能夠提高效率。
  • 不用重新初始化物件,而是動態地獲得物件執行時的狀態。
  • 如果原始物件發生變化(增加或者減少屬性),其它克隆物件的也會發生相應的變化,無需修改程式碼。
3.2 缺點
  • 每一個類都必須要配備一個clone方法。配備clone方法需要對類的功能進行通盤考慮,這對於全新的類來說並不是很難,但是對於已有的類來說並不容易。

4、淺克隆和深克隆

4.1 定義
  • Object類的clone()方法使用的是淺克隆。淺克隆對於要克隆的物件,會複製其基本資料型別String型別的屬性的值給新的物件,而對於引用型別,例如陣列、集合、引用物件等,僅僅複製一份引用給新產生的物件,即新產生的物件和原始物件中的非基本資料型別的屬性都指向的是同一個物件。
  • 深克隆除了要克隆基本資料型別之外,還需要克隆引用型別的資料。深克隆把要複製的物件所引用的物件都複製了一遍,而這種對被引用到的物件的複製叫做間接複製。
  • 深克隆要深入到多少層,是一個不易確定的問題。在決定以深度克隆的方式複製一個物件的時候,必須決定對間接複製的物件時採取淺克隆還是繼續採用深克隆。因此,在採取深克隆時,需要決定多深才算深。此外,在深克隆的過程中,很可能會出現迴圈引用的問題,必須小心處理。
4.2 例項

4.2.1 被引用的類

public class DeepTarget implements Cloneable,Serializable {

    private String name;

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

    public String getName(String name) {
        return name;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
複製程式碼

4.2.2 抽象原型

public abstract class Prototype implements Cloneable {

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
複製程式碼

4.2.3 具體原型

public class ConcretePrototype extends Prototype implements Serializable {

    public DeepTarget deepTarget;

    // 淺克隆
    @Override
    public Object clone() throws CloneNotSupportedException {
        return (ConcretePrototype) super.clone();
    }

    // 深克隆,方式一,使用clone方法
    public Object deepCloneOne() throws CloneNotSupportedException {
        ConcretePrototype prototype = (ConcretePrototype) super.clone();
        prototype.deepTarget = (DeepTarget)deepTarget.clone();
        return prototype;
    }

    // 深克隆,方式二,通過物件的序列化實現(推薦)
    public Object deepCloneTwo() throws IOException,ClassNotFoundException {
        // 將物件寫到流裡
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        // 從流中讀取出來
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
}
複製程式碼

4.2.3 客戶端呼叫

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException,IOException,ClassNotFoundException {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.deepTarget = new DeepTarget();
        prototype.deepTarget.setName("freedom");

        // 淺克隆
        ConcretePrototype concretePrototype = (ConcretePrototype) prototype.clone();
        System.out.println("prototype.hashCode=" + prototype.hashCode() + "\t deepTarget.hashCode=" + prototype.deepTarget.hashCode());
        System.out.println("concretePrototype.hashCode=" + concretePrototype.hashCode() + "\t deepTarget.hashCode=" + concretePrototype.deepTarget.hashCode());
        System.out.println("------------------------------------------------------");

        // 深克隆,方式一
        ConcretePrototype concretePrototype1 = (ConcretePrototype) prototype.deepCloneOne();
        System.out.println("prototype.hashCode=" + prototype.hashCode() + "\t deepTarget.hashCode=" + prototype.deepTarget.hashCode());
        System.out.println("concretePrototype1.hashCode=" + concretePrototype1.hashCode() + "\t deepTarget.hashCode=" + concretePrototype1.deepTarget.hashCode());
        System.out.println("------------------------------------------------------");

        // 深克隆,方式二
        ConcretePrototype concretePrototype2 = (ConcretePrototype) prototype.deepCloneTwo();
        System.out.println("prototype.hashCode=" + prototype.hashCode() + "\t deepTarget.hashCode=" + prototype.deepTarget.hashCode());
        System.out.println("concretePrototype2.hashCode=" + concretePrototype2.hashCode() + "\t deepTarget.hashCode=" + concretePrototype2.deepTarget.hashCode());
    }
}
複製程式碼

特別宣告:1、如若文中有錯之處,歡迎大神指出。 2、文章是參考網上一些大神的文章,自己整理出來的,如若有侵權,可聯絡我刪除。