java設(shè)計(jì)模式-原型模式(Prototype)

定義

原型模式屬于對(duì)象的創(chuàng)建模式。通過給出一個(gè)原型對(duì)象來指明所有創(chuàng)建的對(duì)象的類型,然后用復(fù)制這個(gè)原型對(duì)象的辦法創(chuàng)建出更多同類型的對(duì)象。這就是原型模式的用意

原型模式的結(jié)構(gòu)

原型模式要求對(duì)象實(shí)現(xiàn)同一個(gè)可以“克隆”自身的接口,遮掩個(gè)就可以通過賦值一個(gè)實(shí)例對(duì)象本身來創(chuàng)建一個(gè)新的實(shí)例。

這樣一來,通過原型實(shí)例創(chuàng)建新的對(duì)象,就不再需要關(guān)心這個(gè)實(shí)例本身的類型,只要實(shí)現(xiàn)了克隆自身的方法,就可以通過這個(gè)方法獲取新的對(duì)象,而無需再去通過new來創(chuàng)建。

原型對(duì)象有兩種表現(xiàn)形式:

  1. 簡單形式
  2. 登記形式

這兩種形式僅僅是原型模式的不同實(shí)現(xiàn)。

簡單形式的原型模式

原型模式

原型模式涉及三個(gè)角色:

  1. 客戶(Client)角色:客戶類提出創(chuàng)建對(duì)象的請(qǐng)求。
  2. 抽象原型(Prototype)角色:這是一個(gè)抽象角色,通常由一個(gè)Java接口或者Java抽象類實(shí)現(xiàn)。此角色給出所有的具體原型類所需的接口。
  3. 具體原型(Concrete Prototype)角色:被復(fù)制的對(duì)象。此角色需要實(shí)現(xiàn)抽象原型角色要求的接口。

示例代碼

抽象原型角色

/**
 * 抽象原型角色
 */
public abstract class Prototype {
    private String id;
    
    public Prototype(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    /**
     * 克隆自身的方法
     * @return 一個(gè)從自身克隆出來的對(duì)象。
     */
    public abstract Prototype clone();
}

具體原型角色

public class ConcreteProtype1 extends Prototype {
    public ConcreteProtype1(String id) {
        super(id);
    }

    public Prototype clone() {
        Prototype prototype = new ConcreteProtype1(this.getId());
        return prototype;
    }
}
public class ConcreteProtype2 extends Prototype {
    public ConcreteProtype2(String id) {
        super(id);
    }

    public Prototype clone() {
        Prototype prototype = new ConcreteProtype2(this.getId());
        return prototype;
    }
}

客戶端

public class Client {
    public static void main(String[] args) {
        ConcreteProtype1 protype1 = new ConcreteProtype1("Protype1");
        ConcreteProtype1 protypeCopy1 = (ConcreteProtype1)protype1.clone();
        System.out.println(protypeCopy1.getId());
        System.err.println(protype1.toString());
        System.err.println(protypeCopy1.toString());

        ConcreteProtype2 protype2 = new ConcreteProtype2("Protype2");
        ConcreteProtype2 protypeCopy2 = (ConcreteProtype2)protype2.clone();
        System.out.println(protypeCopy2.getId());
        System.err.println(protype2.toString());
        System.err.println(protypeCopy2.toString());
    }
}

輸出結(jié)果:

Protype1
com.sschen.prototype.ConcreteProtype1@2a139a55
com.sschen.prototype.ConcreteProtype1@15db9742
Protype2
com.sschen.prototype.ConcreteProtype2@6d06d69c
com.sschen.prototype.ConcreteProtype2@7852e922

還有另外一種對(duì)象的復(fù)制方式,如下:

        ConcreteProtype1 protype3 = new ConcreteProtype1("Protype3");
        ConcreteProtype1 protypeCopy3 = protype3;
        System.out.println(protypeCopy3.getId());
        System.err.println(protype3.toString());
        System.err.println(protypeCopy3.toString());

這種方式的輸出結(jié)果為:

Protype3
com.sschen.prototype.ConcreteProtype1@2a139a55
com.sschen.prototype.ConcreteProtype1@2a139a55

這種方式同上面的原型模式的克隆模式比較,可以看見的是:原型模式生成的兩個(gè)對(duì)象,內(nèi)存地址是不一樣的。

之前在java面試 內(nèi)存中堆和棧的區(qū)別文章中說明過:對(duì)象的值存放在堆中,在棧中存儲(chǔ)指向堆中內(nèi)存位置的引用。

第一種方式中,我們是重新創(chuàng)建了新的對(duì)象,對(duì)象的值和引用都是新的,對(duì)于克隆對(duì)象的任何變更,都不會(huì)影響到原對(duì)象的值。如下圖:

原型模式存儲(chǔ)結(jié)構(gòu)圖

另一種方式,我們只是在棧中新創(chuàng)建了一個(gè)引用,指向的還是被復(fù)制對(duì)象對(duì)應(yīng)的堆地址,對(duì)于復(fù)制對(duì)象的變更,都會(huì)同時(shí)改變?cè)瓕?duì)象的值。如下圖:

不正確的對(duì)象復(fù)制方式

登記形式的原型模型

登記形式的原型模型

作為原型模式的第二種形式,它多了一個(gè)原型管理器(PrototypeManager)角色。該角色的作用是:創(chuàng)建具體有原型類的對(duì)象,并記錄每一個(gè)被創(chuàng)建的對(duì)象。

示例代碼

抽象原型角色

public interface Prototype {
    public Prototype clone();
    public String getName();
    public void setName(String name);
}

具體原型角色

public class ConcretePrototype1 implements Prototype {

    private String name;
    
    @Override
    public Prototype clone() {
        ConcretePrototype1 prototype1 = new ConcretePrototype1();
        prototype1.setName(this.name);
        return prototype1;
    }

    @Override
    public String getName() {
        return this.name;
    }

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

    @Override
    public String toString() {
        return "ConcretePrototype1 [name=" + name + "]";
    }
}
public class ConcretePrototype2 implements Prototype {

    private String name;

    @Override
    public Prototype clone() {
        ConcretePrototype2 prototype2 = new ConcretePrototype2();
        prototype2.setName(this.name);
        return prototype2;
    }

    @Override
    public String getName() {
        return this.name;
    }

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

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

原型管理器角色保持一個(gè)聚集,作為對(duì)所有原型對(duì)象的登記,這個(gè)角色提供必要的方法,供外界增加新的原型對(duì)象和取得已經(jīng)登記過的原型對(duì)象。

public class PrototypeManager {
    /**
     * 用來記錄原型的編號(hào)同原型實(shí)例的對(duì)象關(guān)系
     */
    private static Map<String, Prototype> map = new HashMap<String, Prototype>();
    
    /**
     * 私有化構(gòu)造方法,避免從外部創(chuàng)建實(shí)例
     */
    private PrototypeManager() {}
    
    /**
     * 向原型管理器里面添加或者修改原型實(shí)例
     * @param prototypeId 原型編號(hào)
     * @param prototype 原型實(shí)例
     */
    public synchronized static void setPrototype(String prototypeId, Prototype prototype) {
        map.put(prototypeId, prototype);
    }
    
    /**
     * 根據(jù)原型編號(hào)從原型管理器里面移除原型實(shí)例
     * @param prototypeId 原型編號(hào)
     */
    public synchronized static void removePrototype(String prototypeId) {
        map.remove(prototypeId);
    }
    
    /**
     * 根據(jù)原型編號(hào)獲取原型實(shí)例
     * @param prototypeId 原型編號(hào)
     * @return 原型實(shí)例對(duì)象
     * @throws Exception 如果根據(jù)原型編號(hào)無法獲取對(duì)應(yīng)實(shí)例,則提示異?!澳M@取的原型還沒有注冊(cè)或已被銷毀”
     */
    public synchronized static Prototype getPrototype(String prototypeId) throws Exception {
        Prototype prototype = map.get(prototypeId);
        
        if (prototype == null) {
            throw new Exception("您希望獲取的原型還沒有注冊(cè)或已被銷毀");
        }
        
        return prototype;
    }
}

客戶端角色

public class Client {
    public static void main(String[] args) {
        try {
            Prototype p1 = new ConcretePrototype1();
            PrototypeManager.setPrototype("p1", p1);
            //獲取原型來創(chuàng)建對(duì)象
            Prototype p3 = PrototypeManager.getPrototype("p1").clone();
            p3.setName("張三");
            System.out.println("第一個(gè)實(shí)例:" + p3);
            
            //有人動(dòng)態(tài)的切換了實(shí)現(xiàn)
            Prototype p2 = new ConcretePrototype2();
            PrototypeManager.setPrototype("p1", p2);
            
            //重新獲取原型來創(chuàng)建對(duì)象
            Prototype p4 = PrototypeManager.getPrototype("p1").clone();
            p4.setName("李四");
            System.out.println("第二個(gè)實(shí)例:" + p4);
            
            //有人注銷了這個(gè)原型
            PrototypeManager.removePrototype("p1");
            
            //再次獲取原型來創(chuàng)建對(duì)象
            Prototype p5 = PrototypeManager.getPrototype("p1").clone();
            p5.setName("王五");
            System.out.println("第三個(gè)實(shí)例:" + p5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果為:

第一個(gè)實(shí)例:ConcretePrototype1 [name=張三]
第二個(gè)實(shí)例:ConcretePrototype2 [name=李四]
java.lang.Exception: 您希望獲取的原型還沒有注冊(cè)或已被銷毀
    at com.sschen.prototyperegist.PrototypeManager.getPrototype(PrototypeManager.java:44)
    at com.sschen.prototyperegist.Client.main(Client.java:26)

兩種形式的比較

簡單形式和登記形式的原型模式各有其長處和短處。

  • 如果需要?jiǎng)?chuàng)建的原型對(duì)象數(shù)據(jù)較少而且比較固定的話,可以采用第一種形式。在這種情況下,原型對(duì)象的引用可以由客戶端自己保存。
  • 如果要?jiǎng)?chuàng)建的原型對(duì)象數(shù)據(jù)不固定的話,可以采用第二種形式。在這種情況下,客戶端不保存對(duì)原型對(duì)象的引用,這個(gè)任務(wù)被交給原型管理器角色。在克隆一個(gè)對(duì)象之前,客戶端可以查看管理員對(duì)象是否已經(jīng)有一個(gè)滿足要求的原型對(duì)象。如果有,可以從原型管理器角色中取得這個(gè)對(duì)象引用;如果沒有,客戶端就需要自行復(fù)制此原型對(duì)象。

Java中的克隆方法

Java中的所有類都是從java.lang.Object類繼承而來的,而Object類提供protected Object clone()方法對(duì)對(duì)象進(jìn)行克隆復(fù)制,子類當(dāng)然也可以把這個(gè)方法置換掉,提供滿足自己需要的復(fù)制方法。對(duì)象的復(fù)制有一個(gè)基本問題,就是對(duì)象通常都有對(duì)其他對(duì)象的引用。當(dāng)使用Object類的clone()方法來復(fù)制一個(gè)對(duì)象時(shí),此對(duì)象對(duì)其他對(duì)象的引用也同時(shí)會(huì)被復(fù)制一份。

java語言提供的Cloneable接口只起一個(gè)作用,就是在運(yùn)行時(shí)期通知Java虛擬機(jī)可以安全的在這個(gè)類上使用clone()方法。通過調(diào)用這個(gè)clone()方法可以得到一個(gè)對(duì)象的復(fù)制。由于Object類本身不實(shí)現(xiàn)Cloneable接口,因此如果所考慮的類沒有實(shí)現(xiàn)Cloneable接口時(shí),調(diào)用clone()方法會(huì)拋出CloneNotSupportedException異常。

克隆滿足的條件

clone()方法將對(duì)象復(fù)制了一份并返還給了調(diào)用者。所謂“復(fù)制”的含義于clone()方法是怎么實(shí)現(xiàn)的含義是一樣的。一般而言,clone()方法滿足以下的描述:

  1. 對(duì)任何的對(duì)象x,都有x.clone() != x。換而言之,克隆對(duì)象和原對(duì)象不是同一個(gè)對(duì)象。
  2. 對(duì)任何的對(duì)象x,都有x.clone().getClass() == x.getClass()。換而言之,克隆對(duì)象同原對(duì)象的類型一致。
  3. 如果對(duì)象xequals()方法定義其恰當(dāng)?shù)脑挘敲?code>x.clone().equals(x)應(yīng)當(dāng)是成立的。

在Java語言的API中,凡是提供了clone()方法的類,都滿足上面的這些條件。Java語言的設(shè)計(jì)師再設(shè)計(jì)自己的clone()方法時(shí),也應(yīng)當(dāng)遵守這三個(gè)條件。一般來說,上面的三個(gè)條件中的前兩個(gè)是必需的,而第三個(gè)是可選的。

淺克隆和深克隆

無論你是自己實(shí)現(xiàn)克隆方法,還是采用Java提供的克隆方法,都存在一個(gè)淺度克隆和深度克隆的問題。

  • 淺度克隆:只負(fù)責(zé)克隆按值傳遞的數(shù)據(jù)(比如基本數(shù)據(jù)類型,String類型),而不是復(fù)制它所引用的對(duì)象。換而言之,所有對(duì)其他對(duì)象的引用都仍然指向原來的對(duì)象。
  • 深度克隆:除了淺度克隆需要克隆的值外,還負(fù)責(zé)克隆引用類型的數(shù)據(jù)。那些引用其他對(duì)象的變量將指向被復(fù)制過的新對(duì)象,而不再是原有的那些被引用的對(duì)象。換而言之,深度克隆要把復(fù)制的對(duì)象所引用的對(duì)象都復(fù)制一遍,而這種對(duì)被引用到的對(duì)象的復(fù)制叫做簡間接復(fù)制。
    深度克隆要深入到多少層,是一個(gè)不易確定的問題。在決定以深度克隆的方式復(fù)制一個(gè)對(duì)象的時(shí)候,必須決定對(duì)間接復(fù)制的對(duì)象是采取淺度克隆還是繼續(xù)采用深度克隆。因此,在采用深度克隆時(shí),需要決定多深才算深。
    此外,在深度克隆的過程中,很可能會(huì)出現(xiàn)循環(huán)引用的問題,必須小心處理。

使用序列化實(shí)現(xiàn)深度克隆

把對(duì)象寫到流里的過程是序列化Serialization的過程;而把對(duì)象從流中讀出來的過程叫反序列化Deserialization過程。應(yīng)當(dāng)指出的是,寫到流里的是對(duì)象的一個(gè)拷貝,原對(duì)象仍然存在于JVM中。

在Java語言里深度克隆一個(gè)對(duì)象,常??梢韵仁箤?duì)象實(shí)現(xiàn)Serializable接口,然后把對(duì)象(實(shí)際上對(duì)象的拷貝)寫到一個(gè)流里(序列化過程),再從流里讀出來(反序列化過程),便可以重建對(duì)象。

public  Object deepClone() throws IOException, ClassNotFoundException{
    //將對(duì)象寫到流里
    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();
}

這樣做的前提就是對(duì)象及對(duì)象內(nèi)部所有引用到的對(duì)象都是可序列化的,否則,就需要仔細(xì)考察那些不可序列化的對(duì)象是否可以設(shè)置成transient,從而將至排除在復(fù)制過程之外。

淺度克隆顯然比深度克隆更容易實(shí)現(xiàn),因?yàn)镴ava語言的所有類都會(huì)繼承一個(gè)clone()方法,而這個(gè)clone()方法所做的正是淺度克隆。

有一些對(duì)象,比如線程Thread對(duì)象或者Socket對(duì)象,是不能簡單復(fù)制或者共享的。不管是使用淺度克隆還是使用深度克隆,只要涉及這樣的間接對(duì)象,就必須把簡介對(duì)象射程transient而不予復(fù)制;或者由程序自行創(chuàng)建出相當(dāng)?shù)耐葘?duì)象,權(quán)且當(dāng)做復(fù)制件使用。

孫大圣的身外身法術(shù)

孫大圣的身外身本領(lǐng)如果在Java語言里使用原型模式來實(shí)現(xiàn)的話,會(huì)怎么樣呢?

首先,齊天大圣The Greatest Sage,即TheGreatestSage類扮演客戶角色。齊天大圣持有一個(gè)猢猻Monkey的實(shí)例,而猢猻就是大圣本尊。Monkey類具有繼承自java.lang.Objectclone()方法,因此,可以通過調(diào)用這個(gè)克隆方法來復(fù)制一個(gè)Monkey實(shí)例。

示例代碼

大圣持有金箍棒的實(shí)例,因此這里定義了一個(gè)金箍棒的類GoldRingedStaff

/**
 * 金箍棒對(duì)象
 */
public class GoldRingedStaff {
    /**
     * 高度
     */
    private float height = 100.00f;
    /**
     * 半徑
     */
    private float radius = 10.00f;
    /**
     * 金箍棒變大方法
     */
    public void grow() {
        this.radius *= 2;
        this.height *= 2;
    }
    /**
     * 金箍棒縮小方法
     */
    public void shrink() {
        this.radius /= 2;
        this.height /= 2;
    }
}

大圣本尊使用Monkey類來表示,這個(gè)類來扮演具體的原型角色:

/**
 * 獼猴類,大圣本尊由獼猴類來表示
 */
public class Monkey implements Cloneable {
    /**
     * 身高
     */
    private int height;
    /**
     * 體重
     */
    private int weight;
    /**
     * 出生日期
     */
    private Date birthDay;
    /**
     * 金箍棒
     */
    private GoldRingedStaff staff;
    /**
     * 構(gòu)造函數(shù),指定創(chuàng)建事件和給定金箍棒
     */
    public Monkey() {
        this.birthDay = new Date();
        this.staff = new GoldRingedStaff();
    }

    /**
     * 克隆方法,直接調(diào)用接口的克隆方法
     */
    public Object clone() {
        Monkey temp = null;
        try {
            temp = (Monkey)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        } finally {
            return temp;
        }
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public Date getBirthDay() {
        return birthDay;
    }

    public void setBirthDay(Date birthDay) {
        this.birthDay = birthDay;
    }

    public GoldRingedStaff getStaff() {
        return staff;
    }

    public void setStaff(GoldRingedStaff staff) {
        this.staff = staff;
    }
}

孫大圣本尊則使用類TheGreatestSage來表示:

public class TheGreatestSage {
    private Monkey monkey = new Monkey();
    
    public void change() {
        Monkey copyMonkey = (Monkey) monkey.clone();
        System.out.println("大圣本尊的生日是:" + monkey.getBirthDay());
        System.out.println("克隆大圣的生日是:" + copyMonkey.getBirthDay());
        System.out.println("大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:" + (monkey == copyMonkey));
        System.out.println("大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:" + (monkey.getStaff() == copyMonkey.getStaff()));
    }
    
    public static void main(String[] args) {
        TheGreatestSage sage = new TheGreatestSage();
        sage.change();
    }
}

當(dāng)運(yùn)行TheGreatestSage類時(shí),首先創(chuàng)建大圣本尊對(duì)象,然后淺度克隆大圣本尊對(duì)象。程序在運(yùn)行時(shí)輸出的信息如下:

大圣本尊的生日是:Wed Jun 28 10:19:53 CST 2017
克隆大圣的生日是:Wed Jun 28 10:19:53 CST 2017
大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:false
大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:true

可以看出,首先,復(fù)制的大圣本尊具有和原始的大圣本尊一樣的birthDay,而本尊對(duì)象不相等,這表明他們二者是克隆關(guān)系;其次,復(fù)制的大圣本尊持有的金箍棒和原始大圣持有的金箍棒為同一個(gè)對(duì)象,這表明二者所持有的金箍棒為同一根,而非兩根。

正如前面所述,繼承自java.lang.Object類的clone()方法是淺度克隆。換而言之,齊天大圣所有化身所持有的金箍棒引用全都是指向一個(gè)對(duì)象的,這與《西游記》中描寫并不一致。要想要糾正這一點(diǎn),就需要考慮使用深度克隆

想要做到深度克隆,就要求所有需要復(fù)制的對(duì)象都實(shí)現(xiàn)java.io.Serializable接口。

實(shí)例代碼,修改為深度克隆后

孫大圣的源代碼

public class TheGreatestSage {
    private Monkey monkey = new Monkey();
    
    public void change() {
        Monkey copyMonkey = null;
        try {
            copyMonkey = (Monkey) monkey.deepClone();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("大圣本尊的生日是:" + monkey.getBirthDay());
        System.out.println("克隆大圣的生日是:" + copyMonkey.getBirthDay());
        System.out.println("大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:" + (monkey == copyMonkey));
        System.out.println("大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:" + (monkey.getStaff() == copyMonkey.getStaff()));
    }
    
    public static void main(String[] args) {
        TheGreatestSage sage = new TheGreatestSage();
        sage.change();
    }
}

在大圣本尊Monkey類中,原有一個(gè)克隆方法clone(),為淺度克隆,因此新增一個(gè)方法deepClone(),為深度克隆。在deepClone()方法中,大圣本尊對(duì)象(一個(gè)拷貝)被序列化,然后又被反序列化。反序列化后的對(duì)象也就成了一個(gè)深度克隆后的對(duì)象。deepClone()方法如下:

/**
 * 獼猴類,大圣本尊由獼猴類來表示
 */
public class Monkey implements Cloneable,Serializable {

    //………………

    /**
     * 克隆方法,直接調(diào)用接口的克隆方法
     */
    public Object clone() {
        Monkey temp = null;
        try {
            temp = (Monkey)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        } finally {
            return temp;
        }
    }
    
    public Object deepClone() throws IOException, ClassNotFoundException {
        //將對(duì)象寫入流中
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);
        //將對(duì)象從流中讀取回來
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        return objectInputStream.readObject();
    }

    //……………………
}

對(duì)于金箍棒GoldRingedStaff類,也讓其實(shí)現(xiàn)java.io.Serializable接口:

public class GoldRingedStaff implements Serializable {

    //………………

}

修改后的代碼運(yùn)行結(jié)果為:

大圣本尊的生日是:Wed Jun 28 11:31:01 CST 2017
克隆大圣的生日是:Wed Jun 28 11:31:01 CST 2017
大圣本尊同克隆大圣是否為同一個(gè)對(duì)象:false
大圣本尊持有的金箍棒 同 克隆大圣持有的金箍棒是否為同一個(gè)對(duì)象:false

從運(yùn)行結(jié)果可以看出,大圣的金箍棒同克隆大圣的金箍棒是不同的對(duì)象。這是因?yàn)槭褂昧?strong>深度克隆,從而將大圣本尊所引用的對(duì)象也都復(fù)制了一遍,其中也包括金箍棒。

原型模式優(yōu)缺點(diǎn)總結(jié)

原型模式的優(yōu)點(diǎn)

原型模式允許在運(yùn)行時(shí)動(dòng)態(tài)的改變具體的實(shí)現(xiàn)類型。原型模式可以在運(yùn)行期間,有客戶來注冊(cè)符合原型接口的實(shí)現(xiàn)類型,也可以動(dòng)態(tài)的改變具體的實(shí)現(xiàn)類型,看起來接口沒有任何變化,但是其實(shí)運(yùn)行的已經(jīng)是另外一個(gè)類實(shí)體了。因?yàn)榭寺∫粋€(gè)原型對(duì)象就類似于實(shí)例化一個(gè)類。

原型模式的缺點(diǎn)

原型模式最主要的缺點(diǎn)是每一個(gè)類都必須要配備一個(gè)克隆方法。配備克隆方法需要對(duì)類的功能進(jìn)行通盤考慮,這對(duì)于全新的類來說并不是很難,但是對(duì)于已有的類來說并不容易,可別是當(dāng)一個(gè)類引用不支持序列化的間接對(duì)象,或者引用含有循環(huán)結(jié)構(gòu)的時(shí)候。

參考

《JAVA與模式》之原型模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容