定义 原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。可以理解为克隆方法克隆对象。
特点 不需要知道任何创建的细节,不调用构造函数
类型 创建型
适用场景 1 2 3 4 ◆类初始化消耗较多资源 ◆new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等) ◆构造函数比较复杂 ◆循环体中生产大量对象时
优点
原型模式性能比直接new一个对象性能高
简化创建对象的过程
缺点
必须配备克隆方法(重写Object的clone方法,否则不会生效,克隆也是原型模式的核心)
对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
对复杂对象的深拷贝、浅拷贝要运用得当
扩展 深克隆 1 创建一个新对象,本体对象的引用类型属性需要进行深克隆,这样它就不会再指向原有对象地址。
浅克隆 1 创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
小结 1 深浅克隆都会在堆中新分配一块区域,它用来指向本体,区别在于对象属性引用的对象是否需要进行克隆(递归性的)
简单需求 某个对象创建的时候相对比较消耗资源,但是这个对象又不得不创建多次,这时可以使用原型模式。原型模式是在内存中进行二进制字节流的拷贝,比new一个对象性能好很多
原型模式实践 使用原型模式之前 邮件类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.design.pattern.prototype;import lombok.Data;@Data public class Mail { private String name; private String address; private String content; }
邮件工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.design.pattern.prototype;import lombok.extern.slf4j.Slf4j;import java.text.MessageFormat;@Slf 4jpublic class MailUtil { public static void sendMail (Mail mail) { String content = "向{0},邮件地址:{1},邮件内容:{2}发送邮件" ; log.info(MessageFormat.format(content,mail.getName(),mail.getAddress(),mail.getContent())); } public static void mailTemplate (Mail mail) { log.info("邮件的模版内容:" + mail.getContent()); } }
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.design.pattern.prototype;import org.junit.Test;public class Client { @Test public void test () { Mail mail = new Mail(); mail.setContent("邮件模版" ); for (int i = 0 ; i < 10 ; i++){ mail.setName("姓名" + i); mail.setAddress("姓名" + i + "@" + "gmail.com" ); mail.setContent("你收到一封邮件" ); MailUtil.sendMail(mail); } MailUtil.mailTemplate(mail); } }
使用原型模式默认方式(浅拷贝) 邮件类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.design.pattern.prototype;import lombok.Data;@Data public class Mail implements Cloneable { private String name; private String address; private String content; @Override protected Object clone () throws CloneNotSupportedException { System.out.println("克隆Mail..." ); return super .clone(); } }
邮件工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.design.pattern.prototype;import lombok.extern.slf4j.Slf4j;import java.text.MessageFormat;@Slf 4jpublic class MailUtil { public static void sendMail (Mail mail) { String content = "向{0},邮件地址:{1},邮件内容:{2}发送邮件" ; log.info(MessageFormat.format(content,mail.getName(),mail.getAddress(),mail.getContent())); } public static void mailTemplate (Mail mail) { log.info("邮件的模版内容:" + mail.getContent()); } }
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.design.pattern.prototype;import org.junit.Test;public class Client { @Test public void test () throws CloneNotSupportedException { Mail mail = new Mail(); mail.setContent("邮件模版" ); for (int i = 0 ; i < 10 ; i++){ Mail mailTemp = (Mail) mail.clone(); mailTemp.setName("姓名" + i); mailTemp.setAddress("姓名" + i + "@" + "gmail.com" ); mailTemp.setContent("你收到一封邮件" ); MailUtil.sendMail(mailTemp); } MailUtil.mailTemplate(mail); } }
使用原型模式默认方式(深拷贝) 1 2 3 4 5 6 7 8 对于被克隆的目标对象中有引用类型的属性时,如果不对该引用类型的属性进行克隆处理,那么该属性对于目标对象和克隆得到的新对象都是同一个,这很容易引起问题,一定要注意。这样情况,只需要对这个属性进行浅拷贝处理即可解决。 @Override protected Object clone () throws CloneNotSupportedException { Mail mail = (Mail) super .clone(); mail.date = (Date) pig.date.clone(); return mail; }
原型模式扩展 实体类
1 2 3 4 5 6 7 8 9 10 11 public abstract class A implements Cloneable { @Override protected Object clone () throws CloneNotSupportedException { return super .clone(); } }
继承类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class B extends A { public static void main (String [] args) { B b = new B(); try { b.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); System.out.println("处理异常" ); } } }
克隆破坏单例 1 2 3 克隆破坏单例的根本原因是,通过反射暴力调用单例类中的clone方法,这样就会得到一个新对象,单例也就变成了多例。 防止克隆破坏单例,只需要让单例类不实现克隆接口即可,或者实现了克隆接口但是克隆方法返回的仍然是同一对象,而不是 处理克隆。
原型模式在源码中的使用 1 可以通过观察Cloneable接口的使用,就可以追踪原型模式是怎样使用的
源码解析1(Object)
1 2 protected native Object clone () throws CloneNotSupportedException ;
源码解析2(ArrayList实现克隆)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class ArrayList <E > extends AbstractList <E > implements List <E >, RandomAccess , Cloneable , java .io .Serializable { public Object clone () { try { @SuppressWarnings ("unchecked" ) ArrayList<E> v = (ArrayList<E>) super .clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0 ; return v; } catch (CloneNotSupportedException e) { throw new InternalError(); } } }
源码解析3(MyBatis的cacheKey)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package org.apache.ibatis.cache;import java.io.Serializable;import java.lang.reflect.Array;import java.util.ArrayList;import java.util.List;public class CacheKey implements Cloneable , Serializable { @Override public CacheKey clone () throws CloneNotSupportedException { CacheKey clonedCacheKey = (CacheKey) super .clone(); clonedCacheKey.updateList = new ArrayList<Object>(updateList); return clonedCacheKey; } }