23天学习23种设计模式——原型模式

前言

类似于《西游记》中的孙悟空拔出猴毛,根据自己的样子变出很多猴子来。或者是《火影忍者》中鸣人使用影分身变出很多个鸣人来。设计模式中的原型模式,也是根据原型(如同孙悟空,鸣人本人就是原型)创建出新的对象。

是什么

原型模式(prototype pattern)是一种创建型模式,使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式UML类图

为什么

原型模式多用于创建复杂的或构造耗时的实例,这样就能高效地创建实例对象,减轻创建对象的成本。

怎么做

在Java中,所有的类都继承自java.lang.Object类,而该类提供了clone()方法,这个本地方法可以将Java对象复制一份。但是需要注意的是,必须要实现标识接口Cloneable来标识这个类可以被复制。

clone()方法

下面通过一个例子来实现原型模式,这是一个实现了Cloneable接口的类,并重载了Object类的clone方法。

/**
 * 实现Cloneable接口表示该类可以被复制
 */
public class Sheep implements Cloneable{

    private String name;
    private Date birthDate;

    public Sheep(String name,Date date) {
        this.name = name;
        this.birthDate = date;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public String getName() {
        return name;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

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

    /**
     * 重载Object类的clone方法
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //  浅复制
        return super.clone();
    }

    @Override
    public String toString() {
        return "Sheep<"+hashCode()+">'s name="+name+" birthdate:"
                +birthDate.toString()+" name hashcode:"+name.hashCode()+" birthdate hashcode:"+birthDate.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        Sheep another = (Sheep) obj;
        return this.name.equals(another.name)&&this.birthDate.compareTo(another.birthDate)==0;
    }
}

现在我们来测试一下上面那个类对象的复制,

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {

        Date birthDate = new Date(1341314415234L);
        String name = "duoli";
        Sheep duoli = new Sheep(name,birthDate);

        Sheep cloneSheep = (Sheep) duoli.clone();

        System.out.println(duoli.toString());
        System.out.println(cloneSheep.toString());

        birthDate.setTime(2124124124L);

        System.out.println(duoli.toString());
        System.out.println(cloneSheep.toString());
    }
}
运行结果

从上面运行结果截图可以知道,原型对象和克隆对象的值是一样的,但是修改原型对象的属性之后,克隆对象的相应属性也被修改了。这是由于浅复制导致的。也就是说克隆对象只复制了原型对象的地址。当该地址对应的原型对象的值发生变化时,有着相同地址的克隆对象的属性也会发生变化。

浅复制

这个问题可以通过深复制来解决,在深复制中,除了对象本身被复制外,对象所有的属性也会被复制。

深复制

对于Sheep类,我们可以进行如下的修改:

  ...
   @Override
    protected Object clone() throws CloneNotSupportedException {
        //Deep Clone
        Sheep sheep = (Sheep) super.clone();
        sheep.birthDate = (Date) birthDate.clone();
        sheep.name = name;
        return sheep;
}
...

虽然,这种方式看起来比较简单,这是由于我们直接使用了引用类型Date类已经实现好了的clone方法。
对于我们自定义的类,往往我们要去实现Cloneable接口,并重写Object类的clone()方法。

我们还可以通过序列化的方式(Serialization)来实现。通过IO流操作把对象写入流中,流中的对象就是原有对象的拷贝,不仅复制了对象本身,而且可以复制其引用的成员属性。所以,将对象写入流中,然后从流中读出来,就能实现深复制了。

下面通过将clone方法中修改为序列化操作来实现深复制:

 @Override
    protected Object clone() throws CloneNotSupportedException {
        //Deep Clone
//        Sheep sheep = (Sheep) super.clone();
//        sheep.birthDate = (Date) birthDate.clone();
//        sheep.a = (A) a.clone();
//        sheep.name = name;
//        return sheep;

        try {
            return deepClone();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
}
 /**
     * 使用IO技术实现深复制
     * @return
     */
    public Sheep deepClone() throws IOException, ClassNotFoundException {

        //将对象写入IO流中
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(this);

        //将对象从IO流中取出来
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Sheep clone = (Sheep) ois.readObject();

        baos.close();
        oos.close();
        bais.close();
        ois.close();

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

推荐阅读更多精彩内容