[DP]Prototype设计模式

设计模式第8讲——原型模式(Prototype 原型模式属于创建型设计模式。 一、什么是原型模式 二、角色组成 三、优缺点 四、应用场景 4.1 生活场景 4.2 java场景 五、代码实现5.0 UML类图5.1 HeroSkill(英雄--具体原型类)5.2 StealManFactory(塞拉斯--客户端)5.3 TestPrototype六、总结 阅读详情

我们的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。继承该类,然后重写需要注入的方法。



设计模式——Prototype模式 学习侯捷C++面向对象高级编程中Prototype模式。 阅读详情

相关推荐

设计模式-Prototype模式(原型模式)

图解设计模式中的原型模式

weixin_44030848的博客 1386

23种设计模式-原型(Prototype)设计模式

23种设计模式-原型(Prototype)设计模式

Hanson的博客 2073

设计模式—原型模式(Prototype)

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节简单说就是先创建一个原型类实例,然后通过克隆的方法来复制一个一样的新对象,这个对象和原来的对象相同或相似核心:通过自带的Clone()方法和ICloneable接口创建跟该对象相同的新对象,不需要知道具体的创建细节。就是具有相同的属性和功能的对象的抽象的集合。类名称首字母大写。多个单词则各个首字母大写;对外公开的方法需要用‘public’修饰符。

一起加油吧~ 4461

设计模式Prototype

 设计模式Prototype(原型)原文:http://www.vincehuston.org/dp/prototype.html定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求

绿色空间---飞鹰的专栏 385

设计模式Prototype模式

作用:当我们需要在不指定类名的情况下生成实例时,可以使用Prototype模式来通过复制生成实例 UML类图: Product接口: public interface Product extends Cloneable { public abstract void use(String s); public abstract Pro...

aishenyan0793的博客 272

DesignPattern.Ch9.Prototype.Note

原型模式(Prototype),用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象[DP]。 “原型模式其实就是从一个对象再创建另外一个可定制的对象,而不需要知道任何创建的细节。” 基础知识this.MemberwiseClone(),MSDN解释:创建对象的浅表副本。方法是创建一个新的对象,然后将该对象的非静态字段复制到该新对象。如果字段是值类型,则对字段执行逐位复制。...

117

java prototype 模式_JAVA设计模式之原型模式(prototype

原型模式:原型模式又叫克隆模式Java自带克隆模式实现克隆模式必须实现Cloneable接口,如果不实现会发生java.lang.CloneNotSupportedException异常当某个类的属性已经设定好需要创建很多相同属性值的对象的时候使用clone模式非常方便使用clone模式不见得比传统的new方式性能高浅克隆和深克隆先看下面的代码,没有实现Cloneable接口packagecom....

weixin_35500917的博客 317

设计模式-Prototype

原型也是一种创建型模式,考虑下面的场景: 一个类有1,2,3,4,5,6,7,8种状态,在运行过程中经过了以下1->2->3->4的状态转换,且进入到状态4时必须要经过1,2,3的状态转换。 在进入状态4后,可以向5,6,7,8状态转换。 此时要对此对象做白盒测试,1->4是一个固定的流程,很显然,从一个状态已经到4的对象开始复制一堆对象开始去做测试比new一堆对象再分别切换到5,6,7,8要

David Cao的专栏 516

[设计模式] 4 原型模式 prototype

设计模式:可复用面向对象软件的基础》(DP)本文介绍原型模式和模板方法模式的实现。首先介绍原型模式,然后引出模板方法模式。 DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其中有一个词很重要,那就是拷贝。可以说,拷贝是原型模式的精髓所在。举个现实中的例子来介绍原型模式。找工作的时候,我们需要准备简历。假设没有打印设备,因此需手写简历,这些...

weixin_30256505的博客 81

Java设计模式 create Prototype

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34419326的博客 76

设计模式C++实现7:原型模式(Prototype

设计模式C++实现7:原型模式(Prototype)第九章

248

原型模式

原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。[DP] 原型模式(Prototype)结构图   原型模式其实就是从一个对象再创建另外一个可定制的对相关,而且不需知道任何创建的细节,下面来看看基本的原型模式代码: 原型类 abstract class Prototype { private string id; ...

qq_37141120的博客 126

DP-原形模式(Prototype Pattern)

原型模式(Prototype Pattern) ——.NET设计模式系列之六 Terrylee,2006年1月 概述 在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。这时如果用工厂模式,则与产品类等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适。那么如何封装这种动态的变化?从而使依赖于这些易变对象的客户程序不随着产品类变化? 意图 用原型实...

weixin_30256505的博客 91

C++设计模式之原型模式(prototype)(创建型)

一、原型(Prototype)模式概述 DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其中有一个词很重要,那就是拷贝(既clone)。可以说,拷贝(clone)是原型模式的精髓所在。其中拷贝分为深拷贝和浅拷贝。浅复制:被复制的对象的所有变量都含有与原来的对象相同的值,而所有对其他对象的引用都仍然指向原来的对象。深复制:把引用对象的变量指向复制过的新对象,而不是...

janeqi1987的专栏 1574

c# 设计模式(1)一 创建型

为了方便阅读,我把一篇设计模式的资料拆分开发,分为三个大的部分,如下: Factory Abstract FactoryBuilderPrototypeSingleton 名称 Factory Method 结构 意图 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。 适用性 当一个类不知道它所必须创...

feiliboos 77

JAVA设计模式之原型模式(prototype

原型模式: 原型模式又叫克隆模式 Java自带克隆模式 实现克隆模式必须实现Cloneable 接口,如果不实现会发生java.lang.CloneNotSupportedException异常 当某个类的属性已经设定好需要创建很多相同属性值的对象的时候使用clone模式非常方便 使用clone模式不见得比传统的new方式性能高 浅克隆和深克隆 先看下面的代码,没有实现Cloneable接口 package com.srr.dp.clone; /** * (原型模式)克隆模式 */ public c

JAVASTG的博客 339
上一篇: [DP]Builder设计模式
下一篇: [DP]Singleton设计模式
HeapStackk
博客等级 码龄12年 11粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值