设计模式(五)——原型模式(加Spring框架源码分析)
原型模式
1 克隆羊问题
现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10
只羊。
2 传统方式解决克隆羊问题
1) 思路分析(图解)

Sheep类
package com.lin.prototype;
public class Sheep {
private String name;
private int age;
private String color;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";
}
}
Client类
package com.lin.prototype;
import com.lin.prototype.puls.Sheep;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("tom", 3, "黑色");
Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
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(sheep1);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
System.out.println(sheep5);
}
}
3 传统的方式的优缺点
1) 优点是比较好理解,简单易操作。
2) 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
3) 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
4) 改进的思路分析
思路:Java 中 Object 类是所有类的根类,Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制一份,但是需要实现 clone 的 Java 类必须要实现一个接口 Cloneable,该接口表示该类能够复制且具有复制的能力 =>原型模式
4 原型模式-基本介绍
基本介绍
1) 原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
2) 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
3) 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
4) 形象的理解:孙大圣拔出猴毛, 变出其它孙大圣
5 原型模式原理结构图-uml 类图

原理结构图说明
1) Prototype : 原型类,声明一个克隆自己的接口
2) ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作
Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
6 原型模式解决克隆羊问题的应用实例
使用原型模式改进传统方式,让程序具有更高的效率和扩展性。
Sheep类
package com.lin.prototype.puls;
import java.io.Serializable;
public class Sheep implements Cloneable, Serializable{
private static final long serialVersionUID = 12L;
private String name;
private int age;
private String color;
private String address = "来自内蒙古";
public Sheep friend;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
}
//克隆该实例,使用默认的clone方法来完成
@Override
protected Sheep clone() {
Sheep sheep = null;
try {
sheep = (Sheep)super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.getMessage());
}
return sheep;
}
}
Client类
package com.lin.prototype.puls;
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 4, "黄色");
sheep.friend = new Sheep("tommon", 4, "黄黑色");
Sheep sheep1 = sheep.clone();
Sheep sheep2 = sheep.clone();
Sheep sheep3 = sheep.clone();
Sheep sheep4 = sheep.clone();
Sheep sheep5 = sheep.clone();
System.out.println(sheep1.hashCode()+"\t"+sheep1+"\t"+sheep1.friend.hashCode());
System.out.println(sheep2.hashCode()+"\t"+sheep2+"\t"+sheep2.friend.hashCode());
System.out.println(sheep3.hashCode()+"\t"+sheep3+"\t"+sheep3.friend.hashCode());
System.out.println(sheep4.hashCode()+"\t"+sheep4+"\t"+sheep4.friend.hashCode());
System.out.println(sheep5.hashCode()+"\t"+sheep5+"\t"+sheep5.friend.hashCode());
}
}
7.1 原型模式在 Spring 框架中源码分析
1) Spring 中原型 bean 的创建,就是原型模式的应用
2) 代码分析+Debug 源码

7 入讨论-浅拷贝和深拷贝
7.1 浅拷贝的介绍
1) 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2) 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
3) 前面我们克隆羊就是浅拷贝
4) 浅拷贝是使用默认的 clone()方法来实现
sheep = (Sheep) super.clone();
7.2 深拷贝基本介绍
1) 复制对象的所有基本数据类型的成员变量值
2) 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
3) 深拷贝实现方式 1:重写 clone 方法来实现深拷贝
4) 深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
8 深拷贝应用实例
1) 使用 重写 clone 方法实现深拷贝
2) 使用序列化来实现深拷贝
代码演示
DeepCloneableTarget类
package com.lin.prototype;
import java.io.Serializable;
public class DeepCloneableTarget implements Cloneable, Serializable{
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
super();
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
@Override
public String toString() {
return "DeepCloneableTarget [cloneName=" + cloneName + ", cloneClass=" + cloneClass + "]";
}
}
DeepProtoType类
package com.lin.prototype; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class DeepProtoType implements Cloneable, Serializable{ public String name; public DeepCloneableTarget deepCloneableTarget; public com.lin.prototype.puls.Sheep friend; public DeepProtoType() {
super();
} // 深拷贝-方式一使用clone方法
@Override
protected DeepProtoType clone() throws CloneNotSupportedException {
Object deep = null;
// 完成基本数据类型拷贝
deep = super.clone();
// 引用数据类型单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone(); return deepProtoType;
} @Override
public String toString() {
return "DeepProtoType [name=" + name + "]";
} // 深拷贝-方式二使用对象的序列化(推荐使用)
public DeepProtoType cloneSerial() {
// 创建流对象
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) {
System.out.println(e.getMessage());
return null;
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
} }
Client类
package com.lin.prototype;
import com.lin.prototype.puls.Sheep;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType sheep = new DeepProtoType();
sheep.name = "tom";
sheep.deepCloneableTarget = new DeepCloneableTarget("tomm", "tommclass");
sheep.friend = new Sheep("tommon", 4, "黄黑色");
// DeepProtoType sheep1 = sheep.clone();
DeepProtoType sheep1 = sheep.cloneSerial();
System.out.println(sheep+"\t"+sheep.friend.hashCode()+"\n"+sheep1+"\t"+sheep1.friend.hashCode());
System.out.println(sheep+"\t"+sheep.deepCloneableTarget.hashCode()+"\n"+sheep1+"\t"+sheep1.deepCloneableTarget.hashCode());
}
}
9 原型模式的注意事项和细节
1) 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2) 不用重新初始化对象,而是动态地获得对象运行时的状态
3) 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
4) 在实现深克隆的时候可能需要比较复杂的代码
5) 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则,这点请同学们注意.
仅供参考,有错误还请指出!
有什么想法,评论区留言,互相指教指教。
设计模式(五)——原型模式(加Spring框架源码分析)的更多相关文章
- 干货分享之spring框架源码分析02-(对象创建or生命周期)
记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新.欢迎大家指正! 环境: spring5.X + idea 之前分析了Spring读取xml文件的所有信息封装成beanDef ...
- laravel框架源码分析(一)自动加载
一.前言 使用php已有好几年,laravel的使用也是有好长时间,但是一直对于框架源码的理解不深,原因很多,归根到底还是php基础不扎实,所以源码看起来也比较吃力.最近有时间,所以开启第5.6遍的框 ...
- Spring Boot源码分析-配置文件加载原理
在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...
- 精尽Spring Boot源码分析 - 配置加载
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- Spring Ioc源码分析系列--Ioc的基础知识准备
Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...
- YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)
YII 框架源码分析 百度联盟事业部——黄银锋 目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 ...
- 精尽Spring Boot源码分析 - SpringApplication 启动类的启动过程
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
随机推荐
- 风炫安全WEB安全学习第十七节课 使用Sqlmap自动化注入(一)
风炫安全WEB安全学习第十七节课 使用Sqlmap自动化注入(一) sqlmap的使用 sqlmap 是一个开源渗透测试工具,它可以自动检测和利用 SQL 注入漏洞并接管数据库服务器.它具有强大的检测 ...
- Modbus 仿真测试工具 Mod_Rssim 详细图文教程
Mod_RSsim是一款轻量级的Modbus从机模拟器,它可以模拟ModBusTCP和ModBusRTU的从机,能够同时模拟254个被控站,软件使用简单方便,可以满足一般的主机调试. 官方网站:www ...
- Java高并发与多线程(一)-----概念
其实之前一直想专门写一篇,单独说一说Java的多线程与高并发,但是一直以来,都没有想到能够用什么比较有趣的表现形式去表达出来,而且网上充斥着很多类似的博客,有好的又不好的,有简介的有繁琐的,所以也一直 ...
- COW技术重置虚拟机
最近使用COW技术做虚拟机重置,结果成功了,特地来给大家分享一下这次的成果! 文章目录 一.COW技术是什么? 二.重置虚拟机步骤 总结 往期杂文 一.COW技术是什么? Copy On Write, ...
- 为什么.NET Standard 仍然有意义?
.NET Standard 是.NET 官方的API规范,可在许多.NET环境中使用.之所以存在,面向.NET Standard 2.0的库提供了最大可能的覆盖范围,并启用了几乎所有现代的.NET功能 ...
- C中的dll 、lib和exe文件
参考:链接1 链接2 DLL 动态链接库(Dynamic Link Library,缩写为DLL),运行时加载是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源.动态链接 ...
- MySQL45讲:一条update语句是怎样执行的
首先创建一张表: create table T(ID int primary key,c int); 如果要更新ID=2这行+1:应该这样写 update T set c=c+1 where ID=2 ...
- HashMap为什么效率高?来看看这个小demo
一.前情回顾:在程序中有时候需要存放对象,容器应运而生.容器分为集合和Map.集合在这里不说,说说Map.Map在英语中是地图的意思,这个名字真是起的好,可以让人顾名思义.Map,就是存放键值对的结构 ...
- 【EXPDP/IMPDP】ORACLE数据泵导入导出案例(expdp & impdp)
概要: 因项目需要,通常需要将生产库下的部分数据抽取并恢复到测试库上 本文主要介绍数据泵导入导出的几种情况以及错误处理 案例环境: rhel-server-6.5-x86_64 oracle 11.2 ...
- 【ORA】ORA-39002,ORA-39070,ORA-29283, ORA-06512,ORA-29283解决办法
今天使用IMPDP导入的时候报了一个错误 ORA-39002: invalid operation ORA-39070: Unable to open the log file. ORA-2928 ...