原型模式
来源:互联网 发布:淘宝护肤品有真的吗 编辑:程序博客网 时间:2024/05/20 03:45
场景
假设现在我需要发送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
本文摘引自:《设计模式之禅 (第二版)》
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型 模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- 原型模式
- AdminEAP框架简介
- 数据结构与算法分析之平衡二叉树的建立
- docker系列二: docker安装Redis
- Java 8: CompletableFuture vs Parallel Stream
- 数据结构之平衡二叉树
- 原型模式
- Java 内存泄漏
- solr 环境搭建
- 1044. Shopping in Mars (25)
- 数据结构之红黑树
- 网络编程
- Linux Kernel ROP
- Android Studio快速开发之道
- 自用基础3-循环