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