Java设计模式——原型模式
Java设计模式——原型模式
面向对象编程,时时刻刻与对象打交道,有时候获取一个实例化的对象非常麻烦。比如一个需要访问数据库关联大量数据表才能得到一个实例,比如对象的属性非常非常多,通过构造函数获取对象需要初始化很多对象,比较麻烦,浪费内存。类似这样的场景就需要原型模式解决问题啦。
原型模式:说白了就是更简单的获取相同或相似的对象实例。可以理解为复制、克隆。
下面了解两个概念
浅克隆:克隆出来的对象实例一模一样,对象的属性如果是引用数据类型,那么他么指向同一个地址值。无论是修改原来的对象,还是修改克隆出来的对象,只要是引用数据类型修改了,那么两个对象同时被修改,因为他们共享同一地址值。
深克隆:克隆出来的对象实例也是一模一样的,但是他们的引用属性也被克隆了,两个对象虽然一模一样,但是没有任何关联。修改其中一个对象,不影响另一个对象。
需求:实现电子发票对象的克隆,电子发票具有票头、票号、名称、单位、公司、颜色、监制印章、公司印章、是否有效等等对象属性。制作一张发票还是很麻烦的,所以利用原型模式快速复制发票对象,高效完成需求。
提供一个原型接口,便于原型管理和业务扩展。
/**
* @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
全部实现了
Prototype
、Cloneable
、Serializable
接口。
clone()
:是Object类的方法,该方法能够快速复制一个对象,属于浅克隆,引用对象属性不复制。
Cloneable接口
:实现该接口才可以调用clone()
方法。
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都是引用数据类型,但是无法证明他们是共享同一个地址值(希望大神解惑)。
深克隆模型图
对于深克隆:深克隆对所有的数据都进行了拷贝,不论是基本数据类型还是引用数据类型。两个对象之间没有任何关联关系,完完全全是独立自主的,虽然他们长得一样。
总结:所有的程序都是为业务服务的,按照业务需求,如果原型模式获取对象更简单,更合理,那么就应该使用原型模式,如果使用原型模式反而让对象的获取更复杂,程序的复杂度提升,可读性下降。就不应该强行使用设计模式。