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

原型模式,顧名思義就是對(duì)現(xiàn)有的一個(gè)對(duì)象進(jìn)行復(fù)制克隆出一個(gè)全新的對(duì)象。被復(fù)制的對(duì)象就叫做原型對(duì)象,復(fù)制出來的克隆對(duì)象和原型對(duì)象具有相同的屬性和方法。

在一下情況我們一般會(huì)考慮使用原型模式來創(chuàng)建對(duì)象:

  • 將對(duì)象交給外部處理的時(shí)候,為了防止外部操作對(duì)象修改數(shù)據(jù)導(dǎo)致其他地方受影響(實(shí)際傳遞的都是對(duì)象的引用,所以如果多個(gè)地方引用了該對(duì)象可能會(huì)造成不必要的麻煩),所以可以考慮使用原型模式來克隆出一個(gè)新的對(duì)象,及我們明確需要一個(gè)全新的對(duì)象。
  • 如果在創(chuàng)建一個(gè)新對(duì)象的時(shí)候,初始化的資源非常的多,可以考慮使用原型模式來對(duì)一個(gè)現(xiàn)有的對(duì)象進(jìn)行克隆。

下面我們看看原型模式的UML圖:

  • Prototype:可以是一個(gè)接口或者抽象類,聲明了clone的能力
  • ConcretePrototype:實(shí)際上具備clone能力的類
  • Client:使用clone功能的客戶端。

我們先看個(gè)例子來感受一下原型模式的使用。
prototype.png
public class Person implements Cloneable{
    private String name;
    private int age;

    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }

    public void run() {
        System.out.println("run ......");
    }

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

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

public class Test {
    public static void main(String[] args) {
        Person p = new Person("zhangshan",34);
        p.run();
        System.out.println(p);

        try {
            Person pClone = (Person) p.clone();
            pClone.run();
            System.out.println(pClone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

運(yùn)行結(jié)果:

run ......
Person{name='zhangshan', age=34}designpattern.prototype.Person@1b6d3586
run ......
Person{name='zhangshan', age=34}designpattern.prototype.Person@4554617c

可以看到,克隆的是一個(gè)全新的對(duì)象,內(nèi)容和原型對(duì)象一模一樣。

我們先討論下兩個(gè)概率:淺拷貝和深拷貝。

淺拷貝:
在克隆一個(gè)對(duì)象的時(shí)候,如果對(duì)象中有引用型數(shù)據(jù),那么只會(huì)拷貝該數(shù)據(jù)的引用地址,并不會(huì)將引用數(shù)據(jù)對(duì)象全部拷貝。
深拷貝:
與淺拷貝不同,在拷貝引用型數(shù)據(jù)的時(shí)候,會(huì)完完全全的將引用型數(shù)據(jù)對(duì)象全部拷貝。
舉個(gè)例子看下它們的區(qū)別:

class Data implements Cloneable{
    private String title;
    private ArrayList<String> contents = new ArrayList<>();

    public void setTitle(String title) {
        this.title = title;
    }

    public void addContent(String content) {
        contents.add(content);
    }

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

    @Override
    public String toString() {
        return "Data{" +
                "title='" + title + '\'' +
                ", contents=" + contents +
                '}';
    }
}

public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        data.setTitle("呵呵");
        data.addContent("呵呵大大");
        System.out.println("原數(shù)據(jù):" + data);

        try {
            Data clone = (Data) data.clone();
            System.out.println("克隆數(shù)據(jù):" + clone);

            System.out.println("-------------修改克隆數(shù)據(jù)---------------");
            clone.setTitle("么么");
            clone.addContent("么么噠");

            System.out.println("修改后原數(shù)據(jù):" + data);
            System.out.println("修改后克隆數(shù)據(jù):" + clone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

打印結(jié)果:

原數(shù)據(jù):Data{title='呵呵', contents=[呵呵大大]}
克隆數(shù)據(jù):Data{title='呵呵', contents=[呵呵大大]}
-------------修改克隆數(shù)據(jù)---------------
修改后原數(shù)據(jù):Data{title='呵呵', contents=[呵呵大大, 么么噠]}
修改后克隆數(shù)據(jù):Data{title='么么', contents=[呵呵大大, 么么噠]}

從結(jié)果看到,盡管克隆了一個(gè)新對(duì)象,但是原型對(duì)象的contents和克隆對(duì)象的contents的內(nèi)容是一樣的。
怎么解決呢?就需要使用到了深拷貝。
稍稍做一些修改

class Data implements Cloneable{
    private String title;
    private ArrayList<String> contents = new ArrayList<>();

    public void setTitle(String title) {
        this.title = title;
    }

    public void addContent(String content) {
        contents.add(content);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Data data = (Data) super.clone();
        data.contents = (ArrayList<String>) contents.clone();
        return data;
    }

    @Override
    public String toString() {
        return "Data{" +
                "title='" + title + '\'' +
                ", contents=" + contents +
                '}';
    }
}

再看看打?。?/p>

原數(shù)據(jù):Data{title='呵呵', contents=[呵呵大大]}
克隆數(shù)據(jù):Data{title='呵呵', contents=[呵呵大大]}
-------------修改克隆數(shù)據(jù)---------------
修改后原數(shù)據(jù):Data{title='呵呵', contents=[呵呵大大]}
修改后克隆數(shù)據(jù):Data{title='么么', contents=[呵呵大大, 么么噠]}

原型對(duì)象和克隆對(duì)象的數(shù)據(jù)不一樣了。
所以在clone對(duì)象的時(shí)候,如果遇到了引用型數(shù)據(jù),需用調(diào)用引用型數(shù)據(jù)的clone()來重新克隆一份新的數(shù)據(jù),而不是引用地址。

我們也是建議如果需要clone對(duì)象的話盡量使用深拷貝,除非你確定你的對(duì)象都是數(shù)值型數(shù)據(jù)。

在java中如果想使用原型模式來創(chuàng)建對(duì)象,首先必須是要實(shí)現(xiàn)Cloneable接口,這只是一個(gè)標(biāo)記接口,里面啥也沒有,然后覆寫clone()方法即可。

?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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