第 7 章 原型模式
第 7 章 原型模式
1、克隆羊问题
克隆羊问题描述
现在有一只羊tom
, 姓名为: tom
,年龄为: 1
, 颜色为:白色,请编写程序创建和tom
羊属性完全相同的10
只羊
传统模式解决克隆羊问题
类图
代码实现
Sheep
:羊的实体类public class Sheep {private String name;private int age;private String color;
Client
:客户端,发出克隆羊的指令public class Client {public static void main(String[] args) {// 传统的方法Sheep sheep = new Sheep("tom", 1, "白色");Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());// ....System.out.println(sheep);System.out.println(sheep2);System.out.println(sheep3);System.out.println(sheep4);System.out.println(sheep5);// ...}}
传统的方式的优缺点
- 优点是比较好理解,简单易操作
- 在创建新的对象时, 总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
- 总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
改进思路
Java
中Object
类是所有类的根类, Object
类提供了一个clone()
方法,该方法可以将一个Java
对象复制一份,但是需要实现clone
的Java
类必须要实现一个接口Cloneable
,该接口表示该类能够复制且具有复制的能力 --> 原型模式
2、原型模式的介绍
- 原型模式(
Prototype
模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型, 创建新的对象 - 原型模式是一种创建型设计模式,允许一个对象在创建另外一个可定制的对象,无需知道如何创建的细节
- 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即
对象.clone()
- 形象的理解:孙大圣拔出猴毛, 变出其它孙大圣
3、原型模式的原理
原型模式原理结构图(uml
类图)
原理结构图说明
Prototype
:原型类,在该类中声明一个克隆自己的接口ConcretePrototype
:具体的原型类,实现一个克隆自己的操作Client
:让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
4、原型模式代码示例
原型模式解决克隆羊问题的应用实例:使用原型模式改进传统方式,让程序具有更高的效率和扩展性
代码实现
Sheep
:羊的实体类public class Sheep implements Cloneable {private String name;private int age;private String color;// 克隆该实例,使用默认的clone方法来完成@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep) super.clone();} catch (Exception e) {System.out.println(e.getMessage());}return sheep;}// ...
Client
:利用原型模式创建对象public class Client {public static void main(String[] args) {System.out.println("原型模式完成对象的创建");Sheep sheep = new Sheep("tom", 1, "白色");Sheep sheep2 = (Sheep) sheep.clone(); // 克隆Sheep sheep3 = (Sheep) sheep.clone(); // 克隆Sheep sheep4 = (Sheep) sheep.clone(); // 克隆Sheep sheep5 = (Sheep) sheep.clone(); // 克隆System.out.println("sheep2 =" + sheep2 + "sheep2.hashCoe=" + sheep2.hashCode());System.out.println("sheep3 =" + sheep3 + "sheep3.hashCoe=" + sheep3.hashCode());System.out.println("sheep4 =" + sheep4 + "sheep4.hashCoe=" + sheep4.hashCode());System.out.println("sheep5 =" + sheep5 + "sheep5.hashCoe=" + sheep5.hashCode());}}
程序运行结果
原型模式完成对象的创建 sheep2 =Sheep [name=tom, age=1, color=白色];sheep2.hashCoe=366712642 sheep3 =Sheep [name=tom, age=1, color=白色];sheep3.hashCoe=1829164700 sheep4 =Sheep [name=tom, age=1, color=白色];sheep4.hashCoe=2018699554 sheep5 =Sheep [name=tom, age=1, color=白色];sheep5.hashCoe=1311053135
5、Spring 原型模式
准备工作
创建实体类
Monster
public class Monster {private Integer id = 10;private String nickname = "牛魔王";private String skill = "芭蕉扇";public Monster() {System.out.println("monster 创建..");}
通过
XML
配置文件 配置Bean
<!-- 这里我们的 scope="prototype" 即 原型模式来创建 --> <bean id="id01" class="com.atguigu.spring.bean.Monster" scope="prototype" />
测试代码
public class ProtoType {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");// 获取monster[通过id获取monster]Object bean = applicationContext.getBean("id01");System.out.println("bean" + bean); // 输出 "牛魔王" .....Object bean2 = applicationContext.getBean("id01");System.out.println("bean2" + bean2); // 输出 "牛魔王" .....System.out.println(bean == bean2); // false// ConfigurableApplicationContext}}
程序运行结果
monster 创建.. beanMonster [id=10, nickname=牛魔王, skill=芭蕉扇] monster 创建.. bean2Monster [id=10, nickname=牛魔王, skill=芭蕉扇] false
Spring 源码追踪
applicationContext.getBean("id01");
方法中先调用getBeanFactory()
获取Bean
工厂,再调用Bean
工厂的getBean(name)
方法获取Bean
实例@Override public Object getBean(String name) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(name); }
getBean(name)
方法调用doGetBean(name, null, null, false);
方法获取Bean
实例@Override public Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false); }
获取
Bean
实例时- 判断
Bean
是否为单例对象:if (mbd.isSingleton())
,若是,则调用bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
获取单例对象 - 判断
Bean
是否为原型对象:else if (mbd.isPrototype())
,若是,则调用bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
获取原型对象
/*** Return an instance, which may be shared or independent, of the specified bean.* @param name the name of the bean to retrieve* @param requiredType the required type of the bean to retrieve* @param args arguments to use if creating a prototype using explicit arguments to a* static factory method. It is invalid to use a non-null args value in any other case.* @param typeCheckOnly whether the instance is obtained for a type check,* not for actual use* @return an instance of the bean* @throws BeansException if the bean could not be created*/ @SuppressWarnings("unchecked") protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {final String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}try {final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dependsOnBean : dependsOn) {if (isDependent(beanName, dependsOnBean)) {throw new BeanCreationException("Circular depends-on relationship between '" +beanName + "' and '" + dependsOnBean + "'");}registerDependentBean(dependsOnBean, beanName);getBean(dependsOnBean);}}// Create bean instance.if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic 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);}else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; " +"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {try {return getTypeConverter().convertIfNecessary(bean, requiredType);}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type [" +ClassUtils.getQualifiedName(requiredType) + "]", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean; }
- 判断
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
其中的mbd
对象有一个名为scope
属性,用于指示Bean
的生命周期
6、深拷贝与浅拷贝
浅拷贝的介绍
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
- 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
- 前面我们克隆羊就是浅拷贝,浅拷贝是使用默认的
clone()
方法来实现:sheep = (Sheep) super.clone();
代码示例
Sheep
:羊的实体类public class Sheep implements Cloneable {private String name;private int age;private String color;private String address = "蒙古";public Sheep friend; // 对象克隆时会如何处理,默认是浅拷贝// 克隆该实例,使用默认的clone方法来完成@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep) super.clone();} catch (Exception e) {System.out.println(e.getMessage());}return sheep;}// ...
Client
:测试代码public class Client {public static void main(String[] args) {System.out.println("原型模式完成对象的创建");Sheep sheep = new Sheep("tom", 1, "白色");sheep.friend = new Sheep("jack", 2, "黑色");Sheep sheep2 = (Sheep) sheep.clone(); // 克隆Sheep sheep3 = (Sheep) sheep.clone(); // 克隆Sheep sheep4 = (Sheep) sheep.clone(); // 克隆Sheep sheep5 = (Sheep) sheep.clone(); // 克隆System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());}}
程序运行结果:可以看到,所有
Sheep
的friend
属性的hashCode
均相同,说明使用Object
类的Clone
方法为浅拷贝原型模式完成对象的创建 sheep2 =Sheep [name=tom, age=1, color=白色, address=蒙古]sheep2.friend=366712642 sheep3 =Sheep [name=tom, age=1, color=白色, address=蒙古]sheep3.friend=366712642 sheep4 =Sheep [name=tom, age=1, color=白色, address=蒙古]sheep4.friend=366712642 sheep5 =Sheep [name=tom, age=1, color=白色, address=蒙古]sheep5.friend=366712642
深拷贝基本介绍
- 复制对象的所有基本数据类型的成员变量值
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
- 深拷贝实现方式 1:重写
clone
方法来实现深拷贝 - 深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
深拷贝应用实例
DeepCloneableTarget
:该类将会以成员变量的形式出现在其他类中,我们要对其实现深拷贝public class DeepCloneableTarget implements Serializable, Cloneable {private static final long serialVersionUID = 1L;private String cloneName;private String cloneClass;// 构造器public DeepCloneableTarget(String cloneName, String cloneClass) {this.cloneName = cloneName;this.cloneClass = cloneClass;}// 因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();} }
DeepProtoType
:该类中演示了两种实现深拷贝的方法public class DeepProtoType implements Serializable, Cloneable {public String name; // String 属性public DeepCloneableTarget deepCloneableTarget;// 引用类型public DeepProtoType() {super();}// 深拷贝 - 方式 1 使用clone 方法@Overrideprotected Object clone() throws CloneNotSupportedException {Object deep = null;// 这里完成对基本数据类型(属性)和String的克隆deep = super.clone(); DeepProtoType deepProtoType = (DeepProtoType) deep;// 对引用类型的属性,进行单独处理deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();return deepProtoType;}// 深拷贝 - 方式2 通过对象的序列化实现 (推荐)public Object deepClone() {// 创建流对象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {// 序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); // 当前这个对象以对象流的方式输出// 反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);DeepProtoType copyObj = (DeepProtoType) ois.readObject(); // 从流中读入对象return copyObj;} catch (Exception e) {// TODO: handle exceptione.printStackTrace();return null;} finally {// 关闭流try {bos.close();oos.close();bis.close();ois.close();} catch (Exception e2) {System.out.println(e2.getMessage());}}}}
Client
:测试代码public class Client {public static void main(String[] args) throws Exception {DeepProtoType p = new DeepProtoType();p.name = "宋江";p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");// 方式1 完成深拷贝DeepProtoType p2 = (DeepProtoType) p.clone();System.out.println("p.name=" + p.name + ";p.deepCloneableTarget.hashCode=" + p.deepCloneableTarget.hashCode());System.out.println("p2.name=" + p2.name + ";p2.deepCloneableTarget.hashCode=" + p2.deepCloneableTarget.hashCode());// 方式2 完成深拷贝 // DeepProtoType p2 = (DeepProtoType) p.deepClone(); // System.out.println("p.name=" + p.name + ";p.deepCloneableTarget.hashCode=" + p.deepCloneableTarget.hashCode()); // System.out.println("p2.name=" + p2.name + ";p2.deepCloneableTarget.hashCode=" + p2.deepCloneableTarget.hashCode());}}
关于 String 的拷贝问题
clone()
方法为浅拷贝方法,使用clone()
方法不是说只能拷贝基本数据类型吗?那为什么String
类型的成员变量无需单独进行拷贝?- 我的理解是:因为
String
的不可变性,对象c1
通过浅拷贝得到对象c2
,假设此时c1
和c2
中有一个String
变量指向堆中同一个String
引用,c1
和c2
但凡其中有一个修改String
对象的值,由于String
的不可变性,便会新创建一个String
,导致String
类型的变量无需单独做clone()
操作
7、原型模式注意事项
原型模式的注意事项和细节
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
- 不用重新初始化对象,可以动态地获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码,其实使用序列化机制实现克隆的代码也不难
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了
ocp
原则
第 7 章 原型模式相关推荐
- 第六章 Caché 设计模式 原型模式
文章目录 第六章 Caché 设计模式 原型模式 定义 使用场景 优点 结构图 描述 示例 初级写法 缺点 中级写法 缺点 高级写法 (浅复制) 浅复制 深复制 完整示例 简历类(复制类) 对象类(工 ...
- 《Android之大话设计模式》--设计模式 创建型模式 第五章:原型模式
原型模式应用场景举例: GG和MM经常在QQ上聊天,但是GG打字的速度慢如蜗牛爬行,每次MM在瞬间完成恢复或者问候是,GG都会很紧张的去尽力快速打字,尽管如此,还是让 MM有些不高心,MM说回复信息 ...
- Java二十三设计模式之-----原型模式
一.原型模式(Prototype) 原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.本小结会通 ...
- 【设计模式】单例模式-生成器模式-原型模式
前面的几种工厂模式,主要用于选择实现,这里的三种模式:单例模式.生成器模式.原型模式,主要用于生成对象,在GoF的划分中,这是创建型的五种模式(不包括简单工厂,前面提到过,这不是一个标准意义上的设计模 ...
- Java的二十三种设计模式(原型模式(Prototype))
原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.本小结会通过对象的复制,进行讲解.在Java中 ...
- 大话设计模式—原型模式
原型模式(Prototype Pattern),用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象:即用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建 ...
- 对象的克隆——原型模式
本文转载自 :http://blog.csdn.net/lovelion/article/details/7424559 张纪中版<西游记>以出乎意料的造型和雷人的台词遭到广大观众朋友的热 ...
- javascript中组合使用构造函数模式和原型模式创建对象
首先来讲一下构造函数创建对象.ECMAScript中的构造函数可用来创建特定类型的对象.请看下面示例了解一下构造函数模式: function Person(name, age, job){this.n ...
- 第 22 章 备忘录模式
第 22 章 备忘录模式 1.游戏角色状态恢复问题 游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力), 当大战Boss后攻击力和防御力下降, 然后从备忘录对象恢复到大战前的状 ...
最新文章
- 一步一步SharePoint 2007之十六:注册并配置一个网站用户
- 【Python】百度翻译的爬虫实现(后篇)
- C#开发微信门户及应用(7)-微信多客服功能及开发集成
- 应用10秒部署、成本降低50% 阿里云serverless容器改写云计算极限
- (zt)svn 随服务器启动
- [.NET] 《Effective C#》快速笔记(四)- 使用框架
- bzero等函数源代码实现
- Tableau画图初学者~新手教程~常见类型图
- Python中统一快速更换变量的名称
- 百位活跃天使投资人名单
- 20190625——特征抽取 主成分分析
- 元素偏移offset的常用属性
- 第十三届蓝桥杯《EDA设计与开发》赛后总结
- 游戏里经常有涉及用户排行榜(金币消费排行榜),怎么设计一个良好的排行榜。
- 考研英语 词根词缀单词81-88
- IT项目管理的六种错误思维
- html输入时从本文框的顶格输入,浙江省绍兴市越城区2017—2018学年八年级第二学期期末语文试卷(15页)-原创力文档...
- Matrixport,交易、借贷和托管,如何打造数字货币界的银行?
- C语言qsort排序
- photoshop 字体小_使用Fontea Photoshop插件将Google字体添加到Photoshop
热门文章
- 刘强东卸任京东 CEO,“二号位”徐雷接棒:三大电商巨头“二把手”正式集齐
- 2022 年“苹果学者”名单公布,4 位华人学生位列其中
- 窃取任意GitHub Actions敏感信息如此简单,只需要分支改个名?
- 确认无疑,.NET 6是迄今为止最快的.NET
- 130 行代码模仿火爆抖音的“蚂蚁呀嘿”特效,你学会了吗?
- 程序员的求生欲有所强?用Python花式哄女友
- B 站 Up 主自制秃头生成器,圆你秃头梦想可好?
- 曾遭周鸿祎全网封杀的 360 猛将 :草根打工到 36 岁身家上亿的逆袭!
- 25 岁的老 Delphi,还值得程序员入手吗?
- “程序员不会数据分析,有什么影响?”资深程序员:基本等于自废武功!