面向对象编程,时时刻刻与对象打交道,有时候获取一个实例化的对象非常麻烦。比如一个需要访问数据库关联大量数据表才能得到一个实例,比如对象的属性非常非常多,通过构造函数获取对象需要初始化很多对象,比较麻烦,浪费内存。类似这样的场景就需要原型模式解决问题啦。
原型模式 :说白了就是更简单的获取相同或相似的对象实例。可以理解为 复制 、 克隆 。
下面了解两个概念
浅克隆 :克隆出来的对象实例一模一样,对象的属性如果是引用数据类型,那么他么指向同一个地址值。无论是修改原来的对象,还是修改克隆出来的对象,只要是引用数据类型修改了,那么两个对象同时被修改,因为他们共享同一地址值。
深克隆 :克隆出来的对象实例也是一模一样的,但是他们的引用属性也被克隆了,两个对象虽然一模一样,但是没有任何关联。修改其中一个对象,不影响另一个对象。
需求:实现电子发票对象的克隆,电子发票具有票头、票号、名称、单位、公司、颜色、监制印章、公司印章、是否有效等等对象属性。制作一张发票还是很麻烦的,所以利用原型模式快速复制发票对象,高效完成需求。
提供一个原型接口,便于原型管理和业务扩展。
/** * @description: 原型模式原型接口 * @author: lmc * @create: 2019-05-29 20:32 **/ public interface Prototype { /** * @description: 获取浅克隆对象 * @return java.lang.Object * @date 2019/5/29 20:35 * @author lmc */ Prototype getShallowCloneInstance() throws CloneNotSupportedException; /** * @description: 获取深克隆对象 * @return com.lmc.gp12380.pattern.prototype.Prototype * @date 2019/5/30 21:15 * @author lmc */ Prototype getDeepCloneInstance(Prototype prototype); }
提供一个工具类,利用序列化实现深克隆;当然,也可以按照业务需求,把需要克隆类的所有引用对象类全部实现浅克隆,这样也是一个深克隆对象了,但是这种方法很麻烦,容易忽略对象没有去实现浅克隆。
/** * @description: 原型工具类 * @author: lmc * @create: 2019-05-30 21:27 **/ public class PrototypeUtil { /** * @description: 通过序列化获取一个深度克隆的对象 * @param prototype * @return com.lmc.gp12380.pattern.prototype.Prototype * @date 2019/5/30 21:34 * @author lmc */ public static Prototype getSerializInstance(Prototype prototype){ try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(prototype); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); Prototype copy = (Prototype)ois.readObject(); bos.flush(); bos.close(); ois.close(); return copy; }catch (Exception e){ e.printStackTrace(); } return null; } }
电子发票印章图片类
/** * @description: 电子发票印章图片 * @author: lmc * @create: 2019-05-29 21:21 **/ public class Image implements Serializable,Prototype,Cloneable { /** * 颜色 */ private String color="red"; /** * 高度 */ private Integer height=10; /** * 宽度 */ private Integer width=8; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public Integer getHeight() { return height; } public void setHeight(Integer height) { this.height = height; } public Integer getWidth() { return width; } public void setWidth(Integer width) { this.width = width; } @Override public String toString() { return "Image{" + "color='" + color + '\'' + ", height=" + height + ", width=" + width + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public Prototype getShallowCloneInstance() throws CloneNotSupportedException { return (Prototype) clone(); } public Prototype getDeepCloneInstance(Prototype prototype) { return PrototypeUtil.getSerializInstance(prototype); } } /** * @description: 电子印章 * @author: lmc * @create: 2019-05-29 21:19 **/ public class Seal implements Serializable,Cloneable,Prototype { /** * 印章名称 */ private String name; /** * 已知图片 */ private Image image; public String getName() { return name; } public void setName(String name) { this.name = name; } public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } @Override public String toString() { return "Seal{" + "name='" + name + '\'' + ", image=" + image + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public Prototype getShallowCloneInstance() throws CloneNotSupportedException { return (Prototype) clone(); } public Prototype getDeepCloneInstance(Prototype prototype) { return PrototypeUtil.getSerializInstance(prototype); } } /** * @description: 电子发票 * @author: lmc * @create: 2019-05-29 20:36 **/ public class Invoice implements Cloneable,Serializable,Prototype { /** * 票头 */ private String ticketHeader; /** * 票号 */ private int ticketNo; /** * 发票联名称 */ private String name; /** * 发票联颜色 */ private String color; /** * 发票联公司 */ private String company; /** * 公司印章 */ private Seal companySeal; /** * 监制印章 */ private Seal supervisedSeal; /** * 是否有效 */ private Boolean effective; public String getTicketHeader() { return ticketHeader; } public void setTicketHeader(String ticketHeader) { this.ticketHeader = ticketHeader; } public int getTicketNo() { return ticketNo; } public void setTicketNo(int ticketNo) { this.ticketNo = ticketNo; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } public Seal getCompanySeal() { return companySeal; } public void setCompanySeal(Seal companySeal) { this.companySeal = companySeal; } public Seal getSupervisedSeal() { return supervisedSeal; } public void setSupervisedSeal(Seal supervisedSeal) { this.supervisedSeal = supervisedSeal; } public Boolean getEffective() { return effective; } public void setEffective(Boolean effective) { this.effective = effective; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Invoice{" + "ticketHeader='" + ticketHeader + '\'' + ", ticketNo=" + ticketNo + ", name='" + name + '\'' + ", color='" + color + '\'' + ", company='" + company + '\'' + ", companySeal=" + companySeal + ", supervisedSeal=" + supervisedSeal + ", effective=" + effective + '}'; } public Prototype getShallowCloneInstance() throws CloneNotSupportedException { return (Prototype)clone(); } public Prototype getDeepCloneInstance(Prototype prototype) { return PrototypeUtil.getSerializInstance(prototype); } }
电子发票Invoice类 包含了印章Seal类;Seal印章类包含了Image类 ;Invoice、Seal、Image全部实现了
Invoice
Seal
Image
Invoice、Seal、Image
Prototype、Cloneable、Serializable接口。
Prototype
Cloneable
Serializable
clone():是Object类的方法,该方法能够快速复制一个对象,属于浅克隆,引用对象属性不复制。
clone()
Cloneable接口:实现该接口才可以调用clone()方法。
Cloneable接口
Serializable接口:类实现该接口,类的对象才能被序列化和反序列化。
测试代码
/** * @description: 原型模式测试 * @author: lmc * @create: 2019-05-29 21:46 **/ public class PrototypeClientTest { public static void main(String[] args) throws CloneNotSupportedException { Invoice invoice=new Invoice(); invoice.setColor("bule"); invoice.setTicketNo(1); invoice.setEffective(false); invoice.setCompanySeal(new Seal()); //测试浅克隆 //Invoice invoiceClone = (Invoice) invoice.getShallowCloneInstance(); // Invoice invoiceClone = (Invoice) invoice.getDeepCloneInstance(invoice); /** * 验证对象是否同一个。 */ System.out.println("invoice_"+invoice.hashCode()); System.out.println("Clone_invoice_"+invoiceClone.hashCode()); System.out.println("验证对象_"+(invoice==invoiceClone)); /** * 验证基本数据类型是否相等。 */ System.out.println("getTicketNo_"+invoice.getTicketNo()); System.out.println("Clone_getTicketNo_"+invoiceClone.getTicketNo()); System.out.println("验证基本数据类型_"+(invoice.getTicketNo()==invoiceClone.getTicketNo())); /** * 验证引用数据类型是否相等 */ System.out.println("getColor_"+invoice.getColor().hashCode()); System.out.println("Clone_getColor_"+invoiceClone.getColor().hashCode()); System.out.println("验证引用数据类型_"+(invoiceClone.getColor() == invoice.getColor())); System.out.println("getCompanySeal_"+invoice.getCompanySeal().hashCode()); System.out.println("Clone_getCompanySeal_"+invoiceClone.getCompanySeal().hashCode()); System.out.println("验证引用数据类型_"+(invoice.getCompanySeal() == invoiceClone.getCompanySeal())); System.out.println("____________________________________________________________"); /** * 验证基本数据类型修改 */ invoice.setTicketNo(2); System.out.println("验证基本数据类型修改"+invoice); System.out.println("验证基本数据类型修改"+invoiceClone); /** * 验证基本数据类型保包装类 */ invoice.setEffective(true); System.out.println("验证基本数据类型包装类"+invoice); System.out.println("验证基本数据类型包装类"+invoiceClone); /** * 验证引用数据类型 */ invoice.getCompanySeal().setName("验证引用数据类型"); System.out.println("验证引用数据类型"+invoice); System.out.println("验证引用数据类型"+invoiceClone); } }
测试深克隆结果
深克隆的对象和原对象 是两个独立的对象,虽然他们的 内容相同 ,但是他们没有任何关联。
测试浅克隆结果
浅克隆的对象和原对象也是两个对象,但是他们的引用属性对象是共享同一个对象的。上面修改公司印章,克隆对象的公司印章也修改了。基本数据类型和基本数据类型的包装类型不共享。
浅克隆模型图
对于浅克隆:栈内存中基本数据类型存储的是值,引用数据类型存储的是引用地址值。浅克隆对所有的基本数据类型进行拷贝,引用数据类型拷贝引用地址值。 注意 :对于基本数据类型包装类型、String类型,还有基本数据类型,只要对其进行赋值操作,就相当于从新创建了对象,之前的对象或者值就成了不可达状态,只能被垃圾回收器回收了。虽然基本数据类型的包装类型和String都是引用数据类型,但是无法证明他们是共享同一个地址值( 希望大神解惑 )。
深克隆模型图
对于深克隆:深克隆对所有的数据都进行了拷贝,不论是基本数据类型还是引用数据类型。两个对象之间没有任何关联关系,完完全全是独立自主的,虽然他们长得一样。
总结:所有的程序都是为业务服务的,按照业务需求,如果原型模式获取对象更简单,更合理,那么就应该使用原型模式,如果使用原型模式反而让对象的获取更复杂,程序的复杂度提升,可读性下降。就不应该强行使用设计模式。
原文链接:https://www.cnblogs.com/programmerkaixin/p/10969745.html