一. 定义与类型

定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。不需要知道任何创建的细节,不调用构造函数

类型:创建型

二.使用场景

类初始化消耗较多资源

new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)

构造函数比较复杂

循环体中生产大量对象时

三.优缺点

优点:

  原型模式性能比直接new一个对象性能高,简化创建过程

缺点:

  必须配备克隆方法,

  对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险

  深拷贝,浅拷贝要运用得当

四. 扩展

深克隆:对于引用类型,如果需要指向不同的对象,而对于某个对象的引用类型的时候,必须要显式的去写对那个属性进行深克隆

浅克隆:

五. Coding

/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:11
**/
public class Mail implements Cloneable {
private String name;
private String emailAddress;
private String content;
public Mail() {
System.out.println("Mail Class Constructor");
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getEmailAddress() {
return emailAddress;
} public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
} @Override
public String toString() {
return "Mail{" +
"name='" + name + '\'' +
", emailAddress='" + emailAddress + '\'' +
", content='" + content + '\'' + super.toString() +
'}';
} @Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("clone mail object");
return super.clone();
}
}
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:17
**/
public class MailUtil {
public static void sendMail(Mail mail) {
String outputContent = "向{0}同学,邮件地址:{1},邮件内容:{2}发送成功";
System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
} public static void saveOriginMailRecord(Mail mail) {
System.out.println("存储originMail记录,originMail:" + mail.getContent());
}
}
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:20
**/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContent("初始化模板");
System.out.println("初始化mail:" + mail);
for (int i = 0; i < 10; i++) {
//克隆的时候,并不会使用原对象的构造器
Mail mailTemp = (Mail) mail.clone();
mailTemp.setName("姓名" + i);
mailTemp.setEmailAddress("姓名" + i + "@qq.com");
mailTemp.setContent("恭喜您,中奖了");
MailUtil.sendMail(mailTemp);
}
MailUtil.saveOriginMailRecord(mail);
}
}

从上面的代码不难看出,其实原型模式就是实现了cloneable接口,在创建一个不同的对象,来完成邮件的发送,而保留了原来的邮件模板。由于clone不会,调用原对象的构造器,所以在效率上比直接new 对象要高。但是,因为在Mail类中的属性都是简单类型,所以在clone的时候,基本上不会出现上面问题。但是看下面一个实体:

/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:33
**/
public class Pig implements Cloneable {
private String name;
private Date birthDay; public Pig(String name, Date birthDay) {
this.name = name;
this.birthDay = birthDay;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Date getBirthDay() {
return birthDay;
} public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
} @Override
public String toString() {
return "Pig{" +
"name='" + name + '\'' +
", birthDay=" + birthDay +
'}';
} @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:34
**/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Date birthDay = new Date(0L);
Pig pig = new Pig("佩奇", birthDay);
Pig pig1 = (Pig) pig.clone();
System.out.println(pig);
System.out.println(pig1); pig.getBirthDay().setTime(6666666666666L);
/**
* 通过上面对birthDay进行操作,如果是浅拷贝,那么pig与pig1的date对象都会改变:
* 因为在浅拷贝的时候,两个对象中的引用对象date,都是引用同一个对象,所以改变了一个,那么两个都会改变
*
* 如果是深拷贝,那么pig与pig1引用的date对象就是不一样的,改变其中一个,对另一个并没有影响。
*
* 由于深克隆,浅克隆的关系,也算是原型模式的一个坑。(原则是,都会使用深克隆,不然就算是给项目埋坑)
*
*/
System.out.println(pig);
System.out.println(pig1);
}
}

输出结果为:

上面的注释和结果,其实也清楚的看到了。我只改变了pig对象中的birthDay,但是pig1中也改变了。如果clone的对象中存在引用类型的对象,那么如果是浅拷贝,拷贝与被拷贝出的对象的引用对象都是指向同一地址的,所以改变其中一个,另一个也会改变。这时候,如果根据需求就必须使用深拷贝。

也就是对于对象中的引用对象也要进行clone。看下面代码:

/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:33
**/
public class Pig implements Cloneable {
private String name;
private Date birthDay; public Pig(String name, Date birthDay) {
this.name = name;
this.birthDay = birthDay;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Date getBirthDay() {
return birthDay;
} public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
} @Override
public String toString() {
return "Pig{" +
"name='" + name + '\'' +
", birthDay=" + birthDay +
'}';
} @Override
protected Object clone() throws CloneNotSupportedException {
Pig pig = (Pig) super.clone(); //深克隆
pig.birthDay = (Date) pig.birthDay.clone();
return pig;
}
}

输出结果:

可以看到,Pig中重写了clone方法,对Date对象也进行了clone,从而使得,引用对象指向不同的地址。所以改变pig中的birthDay,对于pig1并没有影响。

由于clone方法,可以用原型模式拷贝来破坏单例模式:

/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:34
**/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { /**
* 使用原型模式,克隆破坏单例模式
*
* 这种情况的解决方式:
* 要么单例模式类不去实现Cloneable接口,要么就重写clone方法,直接返回getInstance()方法,这个对象的实例
*/
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
method.setAccessible(true);
HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
System.out.println(hungrySingleton);
System.out.println(cloneHungrySingleton);
}
}

六. 源码分析

基本上只要知道了哪些类使用了Cloneable就知道,原型模式如何使用。

比如ArrayList,HashMap类都重写了clone方法

Mybatis中的CacheKey类也重写了clone()方法

java设计模式——原型模式的更多相关文章

  1. 【设计模式】Java设计模式 - 原型模式

    [设计模式]Java设计模式 - 原型模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

  2. 我的Java设计模式-原型模式

    "不好意思,我是卧底!哇哈哈哈~"额......自从写了上一篇的观察者模式,就一直沉浸在这个角色当中,无法自拨.昨晚在看<使徒行者2>,有一集说到啊炮仗哥印钞票,我去, ...

  3. Java设计模式—原型模式

    原型设计模式是一种比较简单的设计模式,在项目中使用的场景非常多. 个人理解: 原型模式实现了对Java中某个对象的克隆功能,即该对象的类必须implements实现Cloneable接口来标识为可被克 ...

  4. Java设计模式-原型模式(Prototype)

    原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象.这就是选型模式的用意. 原型模式的结构 原型模式要求对象实现一个可以“克 ...

  5. java设计模式---原型模式

    原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式结构图 通俗来说:原型模式就是深拷贝和浅拷贝的实现. 浅拷贝 只实现了值拷贝,对于引用对象还是 ...

  6. 4.java设计模式-原型模式(prototype)

    在<JAVA与模式>一书中开头是这样描述原型(Prototype)模式的: 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更 ...

  7. Java设计模式原型模式

    原型模式: – 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式. – 就是java中的克隆技术,以某个对象为原型,复制出新的对象.显然,新的对象具备原型对象的特点 – 优势 ...

  8. PHP 设计模式 原型模式(Prototype)之深/浅拷贝

      看PHP 设计模式 原型模式(Prototype)时,衍生出一个扩展问题之 原型拷贝的浅拷贝和深拷贝问题(不管写Java还是写PHP还是写JS时都多多少少遇到过对象拷贝问题)   比如写前端页面时 ...

  9. 10. 星际争霸之php设计模式--原型模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

随机推荐

  1. 再看thinkphp5分页类使用

    之前使用tp5的分页paginate类时只用到了第一个参数,也就是每页显示多少行 今天又仔细看了下手册和paginate类,发现paginate可传入的参数有很多,可以满足更多需求 比如可以指定分页的 ...

  2. php数组处理函数

    array_reverse()数组反向排序,$arr=array_reverse($arr)

  3. Bellman_Ford算法(求一个点到任意一点的最短距离)

    单源最短路问题是固定一个起点,求它到任意一点最短路的问题. 记从起点出发到顶点 i 的最短距离为d[i],则有以下等式成立 d[i]=min{d[j]+(从j到 i 的边的权值) 看代码 #inclu ...

  4. matlab 基本操作

    导入excel 右键excel文件, import data, 选择column vector点击导入即可, 在右侧的workspace就可以看到添加的列变量了 在workspace中右键添加clas ...

  5. Chapter 18 MySQL NDB Cluster 7.3 and NDB Cluster 7.4渣翻

    Table of Contents 18.1 NDB Cluster Overview      18.2 NDB Cluster Installation      18.3 Configurati ...

  6. .net中Response.End() 和Response.Redirect("http://dotnet.aspx.cc");

    问:什么情况下需要Response.End()语句,加这句有什么好处 答: 首先你要理解Response.End()的意思,它的意思是终止执行下面的语句!但有时不加和加上都一样,但还要加上好,为什么呢 ...

  7. AD Framework 发布(一)

      1.     EF Code First 发布时,需要配置数据库账号,账号需要存在服务器角色中具备 diskadmin,public,sysadmin 权限. 2.    数据库不存在时,会通过数 ...

  8. IoC和AOP使用扩展。。。

    实现依赖的多种方式. 1.理解构造注入. 2.掌握使用p命名空间实现属性注入. 3.理解不同的数据类型的注入方式. 4.如何通过构造注入为业务类注入所依赖的数据访问层对象,实现保存用户数据功能. 5. ...

  9. 30分钟学会React Hook, Memo, Lazy

    我们来学习React 16.8里的新特性. 1. 自行配置好React的环境,推荐你使用Create React APP, 你也可以下载本文档Zip解压到本地直接运行. https://github. ...

  10. springboot集成freemarker 配置application.properties详解

    #配置freemarker详解 #spring.freemarker.allow-request-override=false # Set whether HttpServletRequest att ...