原型模式

场景

假设现在我需要发送1000W封邮件,我使用多线程发送邮件(单线程的话,发完邮件我也入土了,时间太久。),我创建了一个邮件对象,每次给邮件对象赋值,然后发送出去,但是使用多线程,就遇到了线程安全问题,当线程一,创建好邮件对象后,还没有进行发送邮件操作时,线程二 又对邮件对象进行了赋值,然后线程一开始发送邮件,此时线程一发送的邮件对象,其实已经是线程二赋值的邮件对象了。那么怎么通过设计模式解决这个问题呢?

答案就是 原型模式!

原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

定义

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

原型模式通用类图

这里写图片描述

原型模式的核心就是clone方法,通过该方法进行对象拷贝,java 提供了一个Cloneable接口来“标示“这个对象是可拷贝的。Cloneable接口一个方法都没有,实现Cloneable接口 覆写clone 方法是覆写Object 类的clone 方法。

原型模式通用源码

public class MoreCopy implements Cloneable {

    @Override
    public MoreCopy clone() {
        MoreCopy moreCopy = null;
        try {
            moreCopy = (MoreCopy) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return moreCopy;
    }  
}

原型模式优点

  • 性能优良
    原型模式是在内存二进制流的拷贝,要比new 一个对象性能好很多,特别是要在一个循环体内产生大量对象的时候,原型模式可以更好的体现优势。
  • 逃避构造函数约束
    这即是优点也是缺点,直接在内存中拷贝,构造函数不会执行。优点是减少了约束,缺点也是减少了约束。需要大家在实际应用时考虑。

原型模式使用场景

  • 资源优化场景:类初始化需要消耗非常多的资源,这个资源包括数据,硬件资源。此时使用原型模式直接从内存中拷贝,省去很多麻烦。

  • 性能和安全要求的场景:通过new产生一个对象需要非常繁琐的数据准备,或者访问权限,则可以利用原型模式。

  • 一个对象多个修改者的场景:一个对象需要提供给其他对象访问,而且各个调用着都可能改变其值,可以考虑使用原型模式拷贝多个对象供调用者使用。

    实际中原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
    

原型模式注意事项

  • 使用原型模式拷贝对象是直接在内存中拷贝,构造函数不会执行。

原型模式扩展:浅拷贝和深拷贝

浅拷贝:

public class MoreCopy implements Cloneable {
    //定义一个私有变量
    private ArrayList<String> arrayList = new ArrayList<String>();

    @Override
    public MoreCopy clone() {
        MoreCopy moreCopy = null;
        try {
            moreCopy = (MoreCopy) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return moreCopy;
    }

    public void setValue(String value) {
        this.arrayList.add(value);
    }

    //  arrayList
    public ArrayList<String> getValue() {
        return this.arrayList;

    }
}

浅拷贝测试:

public class Client {
    public static void main(String[] args) {
        MoreCopy moreCopy = new MoreCopy();
        moreCopy.setValue("abel");
        MoreCopy clone = moreCopy.clone();
        clone.setValue("an");

        System.out.println(moreCopy.getValue());
    }
}

运行结果

[abel, an]

很奇怪?不是应该只有 [abel] 么?怎会有[an]呢?是因为java做了一个偷懒的拷贝动作,Object 提供的clone 方法只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝。还是指向原生对象的内部元素地址,这种拷贝叫做浅拷贝。

注意:(其他的原始类型比如:int、long、char等都会被拷贝。对于String 类型 java希望你把它也也认为是基本类型,他是没有clone 方法的,处理机制也比较特殊,通过字符串池在需要的时候才在内存中创建新的字符串,在这里读者就把字符串当作基本类型使用即可)
引用的成员变量必须满足两个条件才不会被拷贝:

一、是累的成员变量,而不是方法内的变量;
二、必须是一个可变的引用对象,而不是一个原始类型或不可变对象。

深入拷贝:

public class MoreCopy implements Cloneable {
    //定义一个私有变量
    private ArrayList<String> arrayList = new ArrayList<String>();

    @Override
    public MoreCopy clone() {
        MoreCopy moreCopy = null;
        try {
            moreCopy = (MoreCopy) super.clone();
            //深拷贝,包括拷贝对象内部的数组、引用对象等
            this.arrayList = (ArrayList<String>)this.arrayList.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return moreCopy;
    }

    public void setValue(String value) {
        this.arrayList.add(value);
    }

    //  arrayList
    public ArrayList<String> getValue() {
        return this.arrayList;

    }
}

仅仅比浅拷贝多了一行代码this.arrayList = (ArrayList<String>)this.arrayList.clone();对私有变量进行了独立拷贝Client没有任何改变,运行结果如下:

[abel]

通过上面方法对私有变量进行了独立拷贝,就实现了完全的拷贝,两个对象之间没有了任何瓜葛,两个对象互不影响,这就是深拷贝。

注意:深拷贝和浅拷贝不要混合使用,特别是涉及继承时,父类有多个引用的情况就非常复杂,建议深拷贝和浅拷贝分开使用。

clone和final 两个冤家

对象的clone 和 对象内的final 关键字是有冲突的。因为final 类型不允许重赋值,但是clone 就是一个重赋值操作,所以clone和final 同时使用时会报错。

本文代码:https://github.com/527515025/Design_pattern
本文摘引自:《设计模式之禅 (第二版)》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值