建立型模式-原型模式(對象的克隆)

1. 定義

原型模式(Prototype Pattern):使用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。原型模式是一種對象建立型模式。git

2. 結構

原型模式的工做原理很簡單:將一個原型對象傳給那個要發動建立的對象,這個要發動建立的對象經過請求原型對象拷貝本身來實現建立過程。因爲在軟件系統中咱們常常會遇到須要建立多個相同或者類似對象的狀況,所以原型模式在真實開發中的使用頻率仍是很是高的。原型模式是一種「另類」的建立型模式,建立克隆對象的工廠就是原型類自身,工廠方法由克隆方法來實現。編程

須要注意的是經過克隆方法所建立的對象是全新的對象,它們在內存中擁有新的地址,一般對克隆所產生的對象進行修改對原型對象不會形成任何影響,每個克隆對象都是相互獨立的。經過不一樣的方式修改能夠獲得一系列類似但不徹底相同的對象。結構如圖:設計模式

原型模式

在原型模式結構圖中包含以下幾個角色:ide

  • Prototype(抽象原型類):它是聲明克隆方法的接口,是全部具體原型類的公共父類,能夠是抽象類也能夠是接口,甚至還能夠是具體實現類。
  • ConcretePrototype(具體原型類):它實如今抽象原型類中聲明的克隆方法,在克隆方法中返回本身的一個克隆對象。
  • Client(客戶類):讓一個原型對象克隆自身從而建立一個新的對象,在客戶類中只須要直接實例化或經過工廠方法等方式建立一個原型對象,再經過調用該對象的克隆方法便可獲得多個相同的對象。因爲客戶類針對抽象原型類Prototype編程,所以用戶能夠根據須要選擇具體原型類,系統具備較好的可擴展性,增長或更換具體原型類都很方便。

3. 代碼實現

1 對象的克隆方法函數

  • 通用實現方法

通用的克隆實現方法是在具體原型類的克隆方法中實例化一個與自身類型相同的對象並將其返回,並將相關的參數傳入新建立的對象中,保證它們的成員屬性相同。代碼以下:ui

public class ConcretePrototype implements Prototype {

    private String attr;

    public String getAttr() {
        return attr;
    }

    public void setAttr(String attr) {
        this.attr = attr;
    }

    @Override
    public Prototype clone() {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAttr(this.attr);
        return concretePrototype;
    }
}
  • Java語言的clone()方法

代碼以下:this

class ConcretePrototype2 implements Cloneable {

    @Override
    public Prototype clone() {
        Object object = null;
        try {
            object = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return (Prototype) object;
    }

}

2 淺克隆與深克隆的實現,代碼以下:設計

public class WeeklyLog implements Cloneable,Serializable {

    private static final long serialVersionUID = 8532521622598526741L;

    private String name;

    private String content;

    private Attachment attachment;

    public String getName() {
        return name;
    }

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

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Attachment getAttachment() {
        return attachment;
    }

    public void setAttachment(Attachment attachment) {
        this.attachment = attachment;
    }

    @Override
    //淺克隆,克隆出來的對象中的Attachment指向同一個對象地址
    public WeeklyLog clone(){
        Object obj;
        try
        {
            obj = super.clone();
            return (WeeklyLog)obj;
        }
        catch(CloneNotSupportedException  e)
        {
            System.out.println("不支持複製!");
            return null;
        }
    }

    //深克隆,克隆出來的對象中的Attachment爲不一樣的對象
    public WeeklyLog deepClone() throws IOException, ClassNotFoundException {
        //將對象寫入流中
        ByteArrayOutputStream bao=new  ByteArrayOutputStream();
        ObjectOutputStream oos=new  ObjectOutputStream(bao);
        oos.writeObject(this);

        //將對象從流中取出
        ByteArrayInputStream bis=new  ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois=new  ObjectInputStream(bis);
        return  (WeeklyLog)ois.readObject();
    }
}



class Attachment {

    private String name;

    public String getName() {
        return name;
    }

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

Java語言提供的Cloneable接口和Serializable接口的代碼很是簡單,它們都是空接口,做爲一種標識,本質上是調用的Object類的方法code

3 利用原型模式可實現一個原型管理器,專門負責克隆相應的對應,代碼以下:

public class PrototypeManager {

    private static PrototypeManager instance = new PrototypeManager();

    private Map<String, OfficialDocument> hm = new ConcurrentHashMap<>();

    private PrototypeManager() {
        hm.put("far", new FAR());
        hm.put("srs", new SRS());
    }

    public PrototypeManager getInstance() {
        return instance;
    }

    //增長新的公文對象
    public void addOfficialDocument(String key, OfficialDocument doc) {
        hm.put(key, doc);
    }

    //經過淺克隆獲取新的公文對象
    public OfficialDocument getOfficialDocument(String key) {
        return ((OfficialDocument) hm.get(key)).clone();
    }

}

//抽象公文接口,也可定義爲抽象類,提供clone()方法的實現,將業務方法聲明爲抽象方法
interface OfficialDocument extends Cloneable {

    OfficialDocument clone();

    void display();
}

//可行性分析報告(Feasibility Analysis Report)類
class FAR implements OfficialDocument {

    @Override
    public OfficialDocument clone() {
        OfficialDocument far = null;
        try {
            far = (OfficialDocument) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("不支持複製!");
        }
        return far;
    }

    @Override
    public void display() {
        System.out.println("《可行性分析報告》");
    }
}

//軟件需求規格說明書(Software Requirements Specification)類
class SRS implements OfficialDocument {

    @Override
    public OfficialDocument clone() {
        OfficialDocument srs = null;
        try {
            srs = (OfficialDocument) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("不支持複製!");
        }
        return srs;
    }

    @Override
    public void display() {
        System.out.println("《軟件需求規格說明書》");
    }
}

4. 優缺點

  • 優勢
  1. 若是建立新的對象比較複雜時,可利用原型模式簡化對象的建立過程,經過複製也可提升效率。
  2. 原型模式提供了簡化的建立結構,無須專門的工廠類來建立產品
  3. 可使用深克隆保持對象的狀態。
  • 缺點
  1. 須要爲每個類配備一個克隆方法,並且該克隆方法位於一個類的內部,當對已有的類進行改造時,須要修改源代碼,違背了「開閉原則」。
  2. 實現深克隆時須要編寫較爲複雜的代碼,多重嵌套引用時實現起來比較麻煩。

5. 適用場景

  1. 建立新對象成本較大(初始化時間較長或佔用資源較長),可經過原型模式對已有對象進行復制獲得。
  2. 若是系統要保存對象的狀態,而對象的狀態變化很小,或者對象自己佔內存不大的時候,也可使用原型模式配合備忘錄模式來應用。相反,若是對象的狀態變化很大,或者對象佔用的內存很大,那麼採用狀態模式會比原型模式更好。
  3. 須要避免使用分層次的工廠類來建立分層次的對象,而且類的實例對象只有一個或不多的幾個組合狀態,經過複製原型對象獲得新實例可能比使用構造函數建立一個新實例更加方便。

6. 我的理解

用來複制一個對象,無需知道具體建立過程,可進行對象的淺克隆或深克隆。

參考

  1. Java設計模式-劉偉
相關文章
相關標籤/搜索