[DP]Prototype设计模式

9 篇文章 0 订阅
8 篇文章 0 订阅

我们的KFG食品连锁店已经开的非常成功了,但是树大招风。我们将面对更加严格的卫生检查。

质检员在我们店里发现了一种新的细菌,这种细菌每1秒能够复制自己一遍(深复制),这种复制速度相当的快,以至于人们是研究它是怎么复制的。

首先我们需要明确一个概念:复制

复制分为 浅复制,深复制。

浅复制是相当轻松的一种复制,在java中,对于primite类型的,浅复制会将起值复制一遍,对于对象和数组,它们本身只是一个引用,所以浅复制只会将引用的值复制一遍,而不会将引用所指向的地址里的东西在复制一遍,所以你能在方法中通过传入对象的引用改变对象,而值传入的primite类型,比如int 你无法在方法内改变起外部值。

深复制是相当耗时耗力的一种复制,深复制会将对象在内存中的表示完全的复制一遍,即开辟一片对等的内存空间,然后Ctrl+C Ctrl+V复制过去。


我们的这种新型细菌原型:

class Bacteria implements Cloneable{
	private String DNAPrototype = "ABCDEFG";
	private int generation = 1;
	private final static double mutaRate = 0.1;
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return null;
	}

}

这里我们实现了 jdk自带的Cloneable接口,实际上里面还没写逻辑。

我们先看一下该细菌所拥有的属性:

DNAPrototype:DNA链原型

generation:产生的第几代

mutaRate:类属性变异率。


然后对Cloneable接口:

public interface Cloneable {
}

卧槽,里面什么都没写,那么怎么复制啊?事实上,这里只是定义了一个规范,具体的实现,其实还是在Object类中。

这里是通过所谓的约定,即 一个类若是实现了Cloneable接口,那么就应该去重写 Object.clone方法,然后将该方法改成public,因为默认在Object.clone方法是protected

 * By convention, classes that implement this interface should override
 * <tt>Object.clone</tt> (which is protected) with a public method.
 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.

既然如此,那么我去Object里面看看 里面的clone方法啊

protected native Object clone() throws CloneNotSupportedException;

卧槽,宝宝再次被吓尿了,这是一个native方法啊,我们看不到实现。不过说实话,因为该方法是在Object,所以需要对所有子类能够有兼容性,针对不同的对象采取一种通用普遍适用的策略。但是我们知道java 语言中本身并没有直接内存直接操作符。所以用java语言实现通用的内存拷贝是行不通的。所以这里提供了一种native方法,即通过C++的直接内存操作,拷贝。




浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。



深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。
深拷贝要深入到多少层,是一个不确定的问题。
在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。
因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。



好了,现在弄明白浅复制和深复制之后,那么Object.clone()方法属于什么拷贝,答案是浅拷贝,在源码文档中已说明

     * Thus, this method performs a "shallow copy" of this object, not a "deep copy" operation.


经过我们认真的分析,比如拿锤子敲开这个细菌,只要刀够快 也能一下将细菌切开两半,我们终于发现其内部构造。

class Bacteria implements Cloneable {
	private String DNAPrototype = "ABCDEFG";
	private int generation = 1;
	private final static double mutaRate = 0.1;

	@Override
	protected Bacteria clone() throws CloneNotSupportedException {
		Bacteria cloneOne = (Bacteria) super.clone();
		cloneOne.generation++;
		int randomNum = (int) (Math.random() * 100);
		if (randomNum < 100 * mutaRate) {
			int muta_index = randomNum % DNAPrototype.length();
			char[] DNAs = DNAPrototype.toCharArray();
			char c = DNAs[muta_index];
			DNAs[muta_index] = DNAs[0];
			DNAs[0] = c;
			cloneOne.DNAPrototype = new String(DNAs);
		}
		return cloneOne;
	}

	@Override
	public String toString() {
		return "Generation:" + generation + " DNAPrototype:" + DNAPrototype;
	}

}

这个细菌根据原版本复制自己,同时因为在复制的过程中存在这变异因素,其DNAPrototype链可能会发生变化。generation 也会更新其信息。

相比细菌 new 一个自己出来,因为这个结构不够复杂,在构建时没有比较耗费时间资源的步骤,所以差异不够显著。

但是如果当在new 的时候 需要耗费比较大的资源来创建对象,那么可以考虑使用clone。

比如如下的横向对比:

class Type implements Cloneable {
	
	@Override
	public Type clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return (Type)super.clone();
	}
}

public class Prototype {
	public static void main(String[] args) throws CloneNotSupportedException {
		long startMili, endMill;
		startMili = System.currentTimeMillis();
		Type t = new Type();
		for (int i = 0; i < 100000; i++)
			 t.clone();
		endMill = System.currentTimeMillis();
		System.out.println("clone operation 100000 times:" + (endMill - startMili));
		startMili = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++)
			new Type();
		endMill = System.currentTimeMillis();
		System.out.println("new operation 100000 times:" + (endMill - startMili));

	}
}

clone operation 100000 times:12

new operation 100000 times:3

如果在简单对象的构建上,new 操作更佳快速直接一点。

但是我们只是在Type的构建过程中增加一些复杂的操作呢?在Type中添加一个比较花费时间的操作。

List<Integer> list;
	public Type() {
		list = new ArrayList<>();
		for(int i = 0; i < 1000; i++)
			list.add(i);
	}

clone operation 100000 times:14

new operation 100000 times:1708


我们看到差异迅速上升。这里主要是由于浅拷贝,共享了list的结果,仅仅只是引用复制过去。

所以根据实际情况 我们因该有所选择的选择clone属性,对于可以共享的比较耗费资源去创建的属性,我们可以将其浅复制。


最后再分析一下Prototype设计模式的组成:












如果要将Spring 和 prototype联系的话,我觉得spring的bean Scope属性可以指定为prototype。

但是这个prototype是否和我们prototype设计模式有所关系呢?


首先我们知道Spring里面获得bean都是getBean()得到的,

然后getBean()调用 AbstractBeanFactory

protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
其中关于bean创建部分如下,主要是单例和原型的创建。

// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								// Explicitly remove instance from singleton cache: It might have been put there
								// eagerly by the creation process, to allow for circular reference resolution.
								// Also remove any beans that received a temporary reference to the bean.
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

然而我们的prototypeInstance = createBean(beanNam,mdb,args);

好的继续追下去。然而createBean方法在该该类中只是一个抽象函数,是在子类AbstractAutowireCapableBeanFactory中实现的

然后createBean继续调用doCreateBean

// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
然后doCreateBean继续调用createBeanInstance。

然后继续调用

return instantiateBean(beanName, mbd);

好的快找到最后我们的prototype是如何创建的了

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
		try {
			Object beanInstance;
			final BeanFactory parent = this;
			if (System.getSecurityManager() != null) {
				beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
					@Override
					public Object run() {
						return getInstantiationStrategy().instantiate(mbd, beanName, parent);
					}
				}, getAccessControlContext());
			}
			else {
				beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
			}
			BeanWrapper bw = new BeanWrapperImpl(beanInstance);
			initBeanWrapper(bw);
			return bw;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
		}
	}

看来是用instantiate来创造bean的啊。

继续。

@Override
	public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (bd.getMethodOverrides().isEmpty()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
								@Override
								public Constructor<?> run() throws Exception {
									return clazz.getDeclaredConstructor((Class[]) null);
								}
							});
						}
						else {
							constructorToUse =	clazz.getDeclaredConstructor((Class[]) null);
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Exception ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}
好的,到了最后BeanUtils.instantiateClass()实际上是通过 类的newInstance来创建的。和我们的prototype设计模式完全是两个概念。



这里值得讲的是  spring中如果采用了方法注入,则会使用CGLib动态增强该bean。继承该类,然后重写需要注入的方法。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值