在有些系统中,存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效。
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。
原型模式包含以下主要角色。
在JAVA里,通过克隆(Clone())方法来实现原型模式。
任何类,要想支持克隆,必须实现一个接口 Cloneable,该接口中有clone()方法,可以在类中重写自定义的克隆方法。
克隆的实现方法有三种:
创建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()方法,较为简单
bean的scope:有一种prototype,容器在接受到该类型对象请求的时候,都会重新生成一个新的对象实例给请求方,该标志内部实现就是实用来了原型模式,完成对象的生成。
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* 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)操作就是原型模式的典型应用,下面对该模式的使用效果和适用情况进行简单的总结。
原型模式的主要优点如下:
原型模式的主要缺点如下:
在以下情况下可以考虑使用原型模式: