java设计模式-原型模式

原型模式,顾名思义就是对现有的一个对象进行复制克隆出一个全新的对象。被复制的对象就叫做原型对象,复制出来的克隆对象和原型对象具有相同的属性和方法。

在一下情况我们一般会考虑使用原型模式来创建对象:

  • 将对象交给外部处理的时候,为了防止外部操作对象修改数据导致其他地方受影响(实际传递的都是对象的引用,所以如果多个地方引用了该对象可能会造成不必要的麻烦),所以可以考虑使用原型模式来克隆出一个新的对象,及我们明确需要一个全新的对象。
  • 如果在创建一个新对象的时候,初始化的资源非常的多,可以考虑使用原型模式来对一个现有的对象进行克隆。

下面我们看看原型模式的UML图:

  • Prototype:可以是一个接口或者抽象类,声明了clone的能力
  • ConcretePrototype:实际上具备clone能力的类
  • Client:使用clone功能的客户端。

我们先看个例子来感受一下原型模式的使用。
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();
        }
    }
}

运行结果:

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

可以看到,克隆的是一个全新的对象,内容和原型对象一模一样。

我们先讨论下两个概率:浅拷贝和深拷贝。

浅拷贝:
在克隆一个对象的时候,如果对象中有引用型数据,那么只会拷贝该数据的引用地址,并不会将引用数据对象全部拷贝。
深拷贝:
与浅拷贝不同,在拷贝引用型数据的时候,会完完全全的将引用型数据对象全部拷贝。
举个例子看下它们的区别:

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("原数据:" + data);

        try {
            Data clone = (Data) data.clone();
            System.out.println("克隆数据:" + clone);

            System.out.println("-------------修改克隆数据---------------");
            clone.setTitle("么么");
            clone.addContent("么么哒");

            System.out.println("修改后原数据:" + data);
            System.out.println("修改后克隆数据:" + clone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

打印结果:

原数据:Data{title='呵呵', contents=[呵呵大大]}
克隆数据:Data{title='呵呵', contents=[呵呵大大]}
-------------修改克隆数据---------------
修改后原数据:Data{title='呵呵', contents=[呵呵大大, 么么哒]}
修改后克隆数据:Data{title='么么', contents=[呵呵大大, 么么哒]}

从结果看到,尽管克隆了一个新对象,但是原型对象的contents和克隆对象的contents的内容是一样的。
怎么解决呢?就需要使用到了深拷贝。
稍稍做一些修改

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 +
                '}';
    }
}

再看看打印:

原数据:Data{title='呵呵', contents=[呵呵大大]}
克隆数据:Data{title='呵呵', contents=[呵呵大大]}
-------------修改克隆数据---------------
修改后原数据:Data{title='呵呵', contents=[呵呵大大]}
修改后克隆数据:Data{title='么么', contents=[呵呵大大, 么么哒]}

原型对象和克隆对象的数据不一样了。
所以在clone对象的时候,如果遇到了引用型数据,需用调用引用型数据的clone()来重新克隆一份新的数据,而不是引用地址。

我们也是建议如果需要clone对象的话尽量使用深拷贝,除非你确定你的对象都是数值型数据。

在java中如果想使用原型模式来创建对象,首先必须是要实现Cloneable接口,这只是一个标记接口,里面啥也没有,然后覆写clone()方法即可。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容