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

原文地址:LoveDev

原型模式是創(chuàng)建型模式的一種爵憎,其特點(diǎn)在于通過“復(fù)制”一個(gè)已經(jīng)存在的實(shí)例來返回新的實(shí)例筑悴,而不是新建實(shí)例人弓。被復(fù)制的實(shí)例就是我們所稱的“原型”鹰椒,這個(gè)原型是可定制的

原型模式
原型模式

使用場景:

  • 通過 new 產(chǎn)生一個(gè)對象需要非常繁瑣的數(shù)據(jù)準(zhǔn)備和訪問權(quán)限
  • 一個(gè)對象需要提供給其他對象訪問锡移,每個(gè)調(diào)用者都有可能修改其屬性,可以原型模式拷貝多個(gè)對象供調(diào)用者使用吹零,即保護(hù)性拷貝
    uml 看起來是不是特別簡單罩抗,用起來其實(shí)也很簡單,Object 類已經(jīng)提供了一個(gè) clone() 方法灿椅,查看源碼可以得知,想要使用該方法钞支,還要實(shí)現(xiàn)一個(gè)標(biāo)識(shí)接口Cloneable 茫蛹,clone() 源碼:
    protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }

        return internalClone();
    }

Java語言提供的Cloneable接口和Serializable接口的代碼非常簡單,它們都是空接口烁挟,這種空接口也稱為標(biāo)識(shí)接口婴洼,標(biāo)識(shí)接口中沒有任何方法的定義,其作用是告訴JRE這些接口的實(shí)現(xiàn)類是否具有某個(gè)功能撼嗓,如是否支持克隆柬采、是否支持序列化等

再來看看超級(jí)簡單的示例代碼:

具體原型類:

public class ConcretePrototype implements Cloneable {

    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "name='" + name + '\'' +
                ", person=" + person +
                '}';
    }
}

看著有沒有特別的簡單方便,但是這里面有一個(gè)坑在且警,這就牽扯到淺拷貝和深拷貝粉捻,上述的原型模式就是淺拷貝,也稱為影子拷貝斑芜,如果需要拷貝的類中全部都是基礎(chǔ)類型的屬性肩刃,淺拷貝也是沒有問題的,但是有引用類型的屬性杏头,就會(huì)出現(xiàn)問題了盈包,出現(xiàn)問題的示例代碼:

具體原型類:

public class Person {

    private String name;
    private String age;

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

// 具體原型類
public class ConcretePrototype implements Cloneable {

    private String name;
    private Person person;

    public String getName() {
        return name;
    }

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

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

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

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "name='" + name + '\'' +
                ", person=" + person +
                '}';
    }
}

Client類:

public class PrototypeActivity extends Activity {

    @BindView(R.id.prototype_text)
    TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_prototype);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.prototype)
    public void prototype_click() {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setName("Kevin");
        Person person = new Person();
        person.setName("Person");
        person.setAge("10");
        concretePrototype.setPerson(person);
        StringBuilder stringBuilder = new StringBuilder("concretePrototype:" + concretePrototype.toString());

        try {
            ConcretePrototype clone = (ConcretePrototype) concretePrototype.clone();
            clone.setName("LoveDev");
            stringBuilder.append("\nclone: ").append(clone.toString());     //第一次修改,正常

            Person newPerson = clone.getPerson();
            newPerson.setName("newPerson");
            newPerson.setAge("20");
            clone.setPerson(newPerson);
            stringBuilder.append("\n\n\nconcretePrototype: ").append(concretePrototype.toString());
            stringBuilder.append("\nclone: ").append(clone.toString());    //第二次修改醇王,被拷貝對象同時(shí)被修改 
            
            stringBuilder.append("\n\n\n 對比兩個(gè)類中的 Person 字段是否相同:").append(concretePrototype.getPerson() == clone.getPerson());

            mTextView.setText(stringBuilder);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

第一次修改的時(shí)候呢燥,只改了具體原型類中的String類型,打印結(jié)果沒有是問題的寓娩,只修改了克隆后的類叛氨,第二次修改Person字段滥朱,再次打印的時(shí)候,發(fā)現(xiàn)原型類已經(jīng)被修改了力试,導(dǎo)致這個(gè)問題的原因是因?yàn)闇\拷貝只是拷貝了引用地址徙邻,兩個(gè)對象指定的是同一內(nèi)存地址,從打印結(jié)果就可以看的出來,要解決這個(gè)問題就要采用深拷貝進(jìn)行克隆撩银,在克隆對象時(shí)腺逛,對于引用類型的字段也要采用克隆的形式,修改后的示例代碼:

public class ConcretePrototype implements Cloneable {

    private String name;
    private Person person;

    public String getName() {
        return name;
    }

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

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.name = this.name;
        concretePrototype.person = (Person) this.person.clone();
        return concretePrototype;
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "name='" + name + '\'' +
                ", person=" + person +
                '}';
    }
}

再次執(zhí)行帅容,可以發(fā)現(xiàn)問題得以解決,原型模式一個(gè)很重要的用途就是保護(hù)性拷貝伍伤,再給其他模塊提供接口訪問一個(gè)不可修改的對象時(shí)并徘,為了防止該模塊負(fù)責(zé)人出于某種原因而修改該對象時(shí),就要用原型模式進(jìn)行保護(hù)性拷貝

優(yōu)點(diǎn):

看了很多的文章都說原型模式效率搞扰魂,下面分別是原型模式和直接new在10000次for循環(huán)中創(chuàng)建對象的內(nèi)存消耗和耗時(shí):

直接new對象麦乞,執(zhí)行耗時(shí)為23毫秒,內(nèi)存占用增加909K劝评,對比圖:


new對象對比
new對象對比

原型模式姐直,執(zhí)行耗時(shí)為44毫秒,內(nèi)存占用增加876K蒋畜,對比圖:


原型模式對比
原型模式對比

從這些數(shù)據(jù)來看声畏,原型模式增加了將近一倍的耗時(shí),內(nèi)存占用并沒有少很多姻成,這還是建立在不太準(zhǔn)確的測試數(shù)據(jù)上插龄,個(gè)人認(rèn)為原型模式在效率方面比直接 new 對象并沒有提高,不過還有其他的優(yōu)點(diǎn)在:

  • 原型模式提供了簡化的創(chuàng)建結(jié)構(gòu)科展,工廠方法模式常常需要有一個(gè)與產(chǎn)品類等級(jí)結(jié)構(gòu)相同的工廠等級(jí)結(jié)構(gòu)均牢,而原型模式就不需要這樣,原型模式中產(chǎn)品的復(fù)制是通過封裝在原型類中的克隆方法實(shí)現(xiàn)的辛润,無須專門的工廠類來創(chuàng)建產(chǎn)品
  • 可以利用深拷貝實(shí)現(xiàn)保護(hù)性拷貝膨处,可實(shí)現(xiàn)撤銷操作

缺點(diǎn):

  • 想要使用原型模式就要在類中實(shí)現(xiàn)克隆方法,修改類的源碼砂竖,違背了“開閉原則”
  • 深拷貝是需要編寫大量的代碼真椿,多層對象嵌套時(shí),每層對象都要支持深拷貝
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乎澄,一起剝皮案震驚了整個(gè)濱河市突硝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌置济,老刑警劉巖解恰,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锋八,死亡現(xiàn)場離奇詭異,居然都是意外死亡护盈,警方通過查閱死者的電腦和手機(jī)挟纱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腐宋,“玉大人紊服,你說我怎么就攤上這事⌒鼐海” “怎么了欺嗤?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卫枝。 經(jīng)常有香客問我煎饼,道長,這世上最難降的妖魔是什么校赤? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任吆玖,我火速辦了婚禮,結(jié)果婚禮上痒谴,老公的妹妹穿的比我還像新娘衰伯。我一直安慰自己,他們只是感情好积蔚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烦周,像睡著了一般尽爆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上读慎,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天漱贱,我揣著相機(jī)與錄音,去河邊找鬼夭委。 笑死幅狮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的株灸。 我是一名探鬼主播崇摄,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慌烧!你這毒婦竟也來了逐抑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤屹蚊,失蹤者是張志新(化名)和其女友劉穎厕氨,沒想到半個(gè)月后进每,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡命斧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年田晚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片国葬。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贤徒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胃惜,到底是詐尸還是另有隱情泞莉,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布船殉,位于F島的核電站鲫趁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏利虫。R本人自食惡果不足惜挨厚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糠惫。 院中可真熱鬧疫剃,春花似錦、人聲如沸硼讽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽固阁。三九已至壤躲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間备燃,已是汗流浹背碉克。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留并齐,地道東北人漏麦。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像况褪,于是被迫代替她去往敵國和親撕贞。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容