Java设计模式-原型模式
原型模式
在有些系统中,存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效。
原型模式的定义与特点
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
原型模式的结构与实现
由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。
1. 模式的结构
原型模式包含以下主要角色。
- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
2. 模式的实现
在JAVA里,通过克隆(Clone())方法来实现原型模式。
任何类,要想支持克隆,必须实现一个接口 Cloneable,该接口中有clone()方法,可以在类中重写自定义的克隆方法。
克隆的实现方法有三种:
- 浅拷贝:浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对直接引用进行拷贝,没有对直接引用指向的对象进行拷贝。
- 深拷贝:深拷贝是指在拷贝对象时,不仅把基本数据类型的变量会重新复制一份,同时会对引用指向的对象进行拷贝。
- 完全拷贝:在包括上面两者共同点的基础上把对象间接引用的对象也进行拷贝。这是最彻底的一种拷贝。通常先使对象序列化,实现Serializable接口,然后将对象写进二进制流里 再从二进制流里读出新对象。
浅拷贝
创建ShallowCopy 对象类 ,重写clone()方法
package prototype;public class ShallowCopy implements Cloneable{private String name;public ShallowCopy(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}protected ShallowCopy clone(){ShallowCopy shallowCopy=null;try {shallowCopy=(ShallowCopy)super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return shallowCopy;}}
测试类:
package prototype;public class Clone {public static void main(String[] args) {shallowCopy();//deepCopy();}public static void shallowCopy(){ShallowCopy shallowCopy = new ShallowCopy("浅拷贝"); //新建一个对象 ,这个对象用作被克隆的原型ShallowCopy copy = shallowCopy.clone();System.out.println("提供的原型对象名称: "+shallowCopy.getName());System.out.println("克隆的的对象名称: "+copy.getName());System.out.println("两个对象"+(shallowCopy == copy?"相同":"不同")); //测试被克隆的对象与原对象是否是同一个对象}
运行结果
提供的原型对象名称: 浅拷贝
克隆的的对象名称: 浅拷贝
两个对象不同
深拷贝
创建DeepCopy 对象类 ,为其添加地址shallowCopy成员对象,重写clone()方法
package prototype;public class DeepCopy implements Cloneable{private String name;ShallowCopy shallowCopy;public DeepCopy(String name, ShallowCopy shallowCopy) {this.name = name;this.shallowCopy = shallowCopy;}public String getName() {return name;}public void setName(String name) {this.name = name;}protected DeepCopy clone(){DeepCopy deepCopy=null;try {deepCopy=(DeepCopy)super.clone();deepCopy.shallowCopy= shallowCopy.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return deepCopy;}
}
测试类:
package prototype;public class Clone {public static void main(String[] args) {//shallowCopy();deepCopy();}public static void deepCopy(){ShallowCopy shallowCopy = new ShallowCopy("浅拷贝");DeepCopy deepCopy = new DeepCopy("深拷贝",shallowCopy);DeepCopy copy = deepCopy.clone();System.out.println("提供的原型对象名称:" + deepCopy.getName() + " 克隆的的对象名称:" + copy.getName());System.out.println("原型对象的引用对象:" + deepCopy.shallowCopy.getName() + " 克隆对象的引用对象:" + copy.shallowCopy.getName());System.out.println("两个对象"+(deepCopy == copy?"相同":"不同")); //测试被可伶的对象与原对象是否是同一个对象System.out.println("修改原型对象名称为深拷贝-1,其引用对象为浅拷贝-1");deepCopy.setName("深拷贝-1");deepCopy.shallowCopy.setName("浅拷贝-1");System.out.println("提供的原型对象名称:" + deepCopy.getName() + " 克隆的的对象名称:" + copy.getName());System.out.println("原型对象的引用对象:" + deepCopy.shallowCopy.getName() + " 克隆对象的引用对象:" + copy.shallowCopy.getName());// 浅复制只复制值类型的变量和对对象的引用// 深复制不仅复制值类型的变量,把原对象引用的对象也进行复制.}}
运行结果
提供的原型对象名称:深拷贝 克隆的的对象名称:深拷贝
原型对象的引用对象:浅拷贝 克隆对象的引用对象:浅拷贝
两个对象不同
修改原型对象名称为深拷贝-1,其引用对象为浅拷贝-1
提供的原型对象名称:深拷贝-1 克隆的的对象名称:深拷贝
原型对象的引用对象:浅拷贝-1 克隆对象的引用对象:浅拷贝
完全拷贝
新建CompleteCopy类,实现 Serializable 接口 创建completeCopy()方法 这里要记住该对象引用的对象 ShallowCopy类也需要实现 Serializable 接口
package prototype;import java.io.*;public class CompleteCopy implements Serializable{private static final long serialVersionUID = -1L;private String name;ShallowCopy shallowCopy;public CompleteCopy(String name, ShallowCopy shallowCopy) {this.name = name;this.shallowCopy = shallowCopy;}public CompleteCopy(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public CompleteCopy completeCopy() throws IOException,ClassNotFoundException,OptionalDataException{CompleteCopy completeCopy=null;// 将对象写入流中ByteArrayOutputStream bao = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bao);oos.writeObject(this);// 将对象从流中取出ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (CompleteCopy) ois.readObject();}}
测试类:
package prototype;import java.io.IOException;public class Clone {public static void main(String[] args) throws IOException, ClassNotFoundException {ShallowCopy shallowCopy = new ShallowCopy("浅拷贝");CompleteCopy completeCopy = new CompleteCopy("完全拷贝",shallowCopy);CompleteCopy copy = completeCopy.completeCopy();System.out.println("提供的原型对象名称:" + completeCopy.getName() + " 克隆的的对象名称:" + copy.getName());System.out.println("原型对象的引用对象:" + completeCopy.shallowCopy.getName() + " 克隆对象的引用对象:" + copy.shallowCopy.getName());System.out.println("修改原型对象名称为完全拷贝-1,其引用对象为浅拷贝-1");completeCopy.setName("完全拷贝-1");completeCopy.shallowCopy.setName("浅拷贝-1");System.out.println("提供的原型对象名称:" + completeCopy.getName() + " 克隆的的对象名称:" + copy.getName());System.out.println("原型对象的引用对象:" + completeCopy.shallowCopy.getName() + " 克隆对象的引用对象:" + copy.shallowCopy.getName());// 浅复制只复制值类型的变量和对对象的引用// 深复制不仅复制值类型的变量,把原对象引用的对象也进行复制.}
}
运行结果
提供的原型对象名称:完全拷贝 克隆的的对象名称:完全拷贝
原型对象的引用对象:浅拷贝 克隆对象的引用对象:浅拷贝
两个对象不同
修改原型对象名称为完全拷贝-1,其引用对象为浅拷贝-1
提供的原型对象名称:完全拷贝-1 克隆的的对象名称:完全拷贝
原型对象的引用对象:浅拷贝-1 克隆对象的引用对象:浅拷贝
由此可见 完全拷贝将某对对象直接引用对象,包括引用对象引用的对象都重新复制,不需要实现Cloneable接口,重写Clone()方法,较为简单
Spring对原型模式的底层实现
bean的scope:有一种prototype,容器在接受到该类型对象请求的时候,都会重新生成一个新的对象实例给请求方,该标志内部实现就是实用来了原型模式,完成对象的生成。
/*** Actually create the specified bean. Pre-creation processing has already happened* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.* <p>Differentiates between default bean instantiation, use of a* factory method, and autowiring a constructor.* @param beanName the name of the bean* @param mbd the merged bean definition for the bean* @param args explicit arguments to use for constructor or factory method invocation* @return a new instance of the bean* @throws BeanCreationException if the bean could not be created* @see #instantiateBean* @see #instantiateUsingFactoryMethod* @see #autowireConstructor*/protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
原型模式的扩展
原型模式可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型,Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型。其结构图如图所示。
在PrototypeManager中定义了一个HashMap类型的集合对象,使用“键值对”来存储原型对象,客户端可以通过Key(如“far”或“srs”)来获取对应原型对象的克隆对象。PrototypeManager类提供了类似工厂方法的getPrototype()方法用于返回一个克隆对象。在实际应用中,可以使用ConcurrentHashMap(Java 5或以上)集合来保证线程安全,将PrototypeManager设计为单例类,使用饿汉式单例实现,确保系统中有且仅有一个PrototypeManager对象,有利于节省系统资源,并可以更好地对原型管理器对象进行控制。
原型模式的扩展
原型模式作为一种快速创建大量相同或相似对象的方式,在软件开发中应用较为广泛,很多软件提供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就是原型模式的典型应用,下面对该模式的使用效果和适用情况进行简单的总结。
1.主要优点
原型模式的主要优点如下:
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
- 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
- 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
- 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
2.主要缺点
原型模式的主要缺点如下:
- 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。
3.适用场景
在以下情况下可以考虑使用原型模式:
- 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
- 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
Java设计模式-原型模式相关推荐
- Java设计模式——原型模式
概述 原型模式是为了解决一些不必要的对象创建过程.当Java JDK中提供了Cloneable接口之后,原型模式就变得异常的简单了.虽然由于Cloneable的引入使用程序变得更简单了,不过还是有一些 ...
- 我的Java设计模式-原型模式
"不好意思,我是卧底!哇哈哈哈~"额......自从写了上一篇的观察者模式,就一直沉浸在这个角色当中,无法自拨.昨晚在看<使徒行者2>,有一集说到啊炮仗哥印钞票,我去, ...
- 7.Java设计模式-----原型模式(Prototype Pattern)
什么是原型模式? 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式是实现了一个原型接口 ...
- Java设计模式--原型模式
1 Prototype Pattern 原型模式 目的:在运行期通过"复制和粘贴"来创建新对象 : 实现:创建一个原型对象,再通过Java 语言中常用的克隆实现方法复制这个原型对象 ...
- java设计模式---原型模式
在阎宏博士的<JAVA与模式>一书中开头是这样描述原型(Prototype)模式的: 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办 ...
- Java设计模式--原型模式Prototype
原型模式Prototype 原型模式使得用户可以通过复制对象样本来创建新对象.与通过调用构造函数创建对象相比,二者主要区别在于:通过复制创建的新对象一般会包含原始对象的某些状态. 原型模式属于对象的创 ...
- Java常用设计模式————原型模式(一)
介绍 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 原型模式用于创建重复的对象,同时又能保证性能.当直接创建对象的代价比较大时,则采用 ...
- 设计模式 原型模式_创新设计模式:原型模式
设计模式 原型模式 原型模式用于创建对象的副本. 这种模式非常有用,特别是当从头开始创建对象的成本很高时. 与builder , factory和abstract factory模式相比,它不会从头开 ...
- 设计模式 原型模式_设计模式:原型
设计模式 原型模式 创新设计模式之一是原型设计模式 . 尽管原型是创造模式,但它在概念上与其他模式有所区别. 我的意思是原型在某种意义上创造了自己. 我将在下面解释. 原型模式的所有魔力都基于Java ...
最新文章
- 【Web前端培训】预解析(变量提升)
- 抖音、快手和直播行业的火爆究竟给了谁机会?
- MSSQL2005 手工盲注 总结
- linux命令 pushd和popd
- 【Java Web开发指南】Mybatis 中的延迟加载
- castle windsor学习----- Services and Components 两者的定义
- svn由于连接方在一段时间后没有正确答复或连接的主机没有反应连接尝试失败...
- 路透社:谷歌已停止与华为部分合作;联想否认断供华为PC;微软计划直供Linux内核;谷歌无人机快递Wing进军芬兰……...
- java面试要点---oracle,mysql,DB2数据库的分页
- SpringMVC工作原理的介绍
- stdio.h头文件被更改怎么办
- 红外图像盲元修正MATLAB算法,红外图像盲元检测方法与流程
- vs 2010下载地址
- Echarts绘制中国地图
- 华硕笔记本开机自动进入bios,进不了windows系统的解决方法
- 带你了解NLP的词嵌入
- HTTPS是什么幺蛾子
- Intellij IDEA 的 Soft-wrap 是什么
- [BIOS] thinkpad X220 BIOS设置详解
- JavaScript扫雷游戏,仿Windows扫雷