当前位置: 首页 > news >正文

设计模式之原型模式

prototype design pattern

原型模式的概念、原型模式的结构、原型模式的优缺点、原型模式的使用场景、浅拷贝与深拷贝、原型模式的实现示例、原型模式的源码分析


1、原型模式的概念

  原型模式,即将一个对象作为原型对象,通过对其克隆而复制出多个与原型对象类似的新实例。

  使用原型模式创建对象要比使用 new 等关键字创建对象性能好的多,因为原型模式的本质是使用 Object 的 clone() 方法克隆对象,而这个方法是本地方法,它直接操作内存中的二进制流,特别是大对象时,性能差异特别明显。使用原型模式就好比复制粘贴,而不是亲手码。需要注意的是,使用原型模式时需要考虑克隆的深、浅对结果的影响。

2、原型模式的结构

  • 原型类实现 Cloneable 接口。
  • 原型类实现 clone() 方法。

prototype-class

3、原型模式的优缺点

3.1、优点

  • 比使用 new 等关键字创建对象在性能上好很多,因为其是本地方法,直接操作内存中的二进制流。
  • 对于相似度高的大对象的创建,效率要高很多。
  • 原型对象在继承时,子类重写父类方法时可先调用父类方法,在扩展,开发效率高。

3.2、缺点

  • 当多个目标对象共享同一个原型对象的引用数据时(如 array、object 等),可能会相互影响。主要原因是浅拷贝与深拷贝的实现不同。

4、原型模式的使用场景

  • 基类的结构和数据都要被复用时。
  • 对目标对象的修改不影响既有的原型对象时(深拷贝时完全不影响)。
  • 当一个对象创建时需要繁琐的数据准备和访问权限时。
  • 当一个对象需要共享给多个对象,且每个对象都有可能修改该对象时。
  • 原型模式很少单独出现,一般和工厂方法模式和抽象工厂模式结合使用,先通过原型模式创建一个对象,然后由工厂方法或抽象工厂提供给调用者使用。

5、深拷贝与浅拷贝

  • 浅拷贝:

    浅拷贝只是在栈上多了一份当前对象的引用,当前对象在堆中只有一份,且地址未发生变化。

  • 深拷贝:

    深拷贝是将当前对象复制了一份,且在堆中分配了内存,从物理层面来说,拷贝结果与原始对象已经是两个东西了,只是长得一样而已。

6、原型模式的实现示例

6.1、浅拷贝实现原型模式

  定义原型类:

public class Prototype implements Cloneable {

    private String username;

    private Integer gender;

    private Prototype friend;

    public Prototype(String username, Integer gender) {
        super();
        this.username = username;
        this.gender = gender;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Prototype getFriend() {
        return friend;
    }

    public void setFriend(Prototype friend) {
        this.friend = friend;
    }

    @Override
    public Prototype clone() {
        try {
            return (Prototype) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

  测试类:

public class PrototypeTest {

    public static void main(String[] args) {
        Prototype prototype = new Prototype("zed", 24);
        prototype.setFriend(new Prototype("fizz", 21));

        Prototype prototype1 = prototype.clone();
        Prototype prototype2 = prototype.clone();

      	System.out.println("prototype = " + prototype + " prototype.friend = "+ prototype.getFriend().hashCode());
        System.out.println("prototype = " + prototype + " prototype.friend = "+ prototype.getFriend().hashCode());
        System.out.println("prototype1 = " + prototype1 + " prototype1.friend = "+ prototype1.getFriend().hashCode());
        System.out.println("prototype2 = " + prototype2 + " prototype2.friend = " + prototype2.getFriend().hashCode());
    }
}

  测试结果:

  可以看到,浅拷贝只是复制了当前对象的指针,也就是说只是在栈上多了一份原对象的引用。

prototype = org.xgllhz.designpattern.createtype.prototype.shallow.Prototype@5034c75a prototype.friend = 748658608
prototype1 = org.xgllhz.designpattern.createtype.prototype.shallow.Prototype@396a51ab prototype1.friend = 748658608
prototype2 = org.xgllhz.designpattern.createtype.prototype.shallow.Prototype@51081592 prototype2.friend = 748658608

6.2、深拷贝实现原型模式

  引用对象:

public class Children implements Cloneable, Serializable {

    private static final long serialVersionUID = 2263495181008794106L;

    private String username;

    private Integer gender;

    public Children(String username, Integer gender) {
        this.username = username;
        this.gender = gender;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    @Override
    public Children clone() {
        try {
          	// 该类属性全是非引用数据类型 故直接使用默认 clone 实现
            return (Children) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

  原型类:

public class Prototype implements Cloneable, Serializable {

    private static final long serialVersionUID = -2375259498572341431L;

    private String username;

    private Children children;

    public Prototype(String username, Children children) {
        super();
        this.username = username;
        this.children = children;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Children getChildren() {
        return children;
    }

    public void setChildren(Children children) {
        this.children = children;
    }

    /**
     * 深拷贝
     * 深拷贝推荐用序列化实现
     * @return
     */
    @Override
    public Prototype clone() {
        ByteArrayOutputStream byteArrayOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            // 序列化
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);

            // 反序列化
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            Object o = objectInputStream.readObject();

            return (Prototype) o;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (objectOutputStream != null) {
                    objectOutputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
                if (objectInputStream != null) {
                    objectInputStream.close();
                }
                if (byteArrayInputStream != null) {
                    byteArrayInputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

  测试类:

public class PrototypeTest {

    public static void main(String[] args) {
        Children children = new Children("Kaisa", 21);
        Prototype prototype = new Prototype("Kassadin", children);

        Prototype prototype1 = prototype.clone();
        Prototype prototype2 = prototype.clone();

        System.out.println("prototype = " + prototype + " prototype.friend = "+ prototype.getChildren().hashCode());
        System.out.println("prototype1 = " + prototype1 + " prototype1.friend = "+ prototype1.getChildren().hashCode());
        System.out.println("prototype2 = " + prototype2 + " prototype2.friend = " + prototype2.getChildren().hashCode());
    }
}

  测试结果:

  可以看到,其引用对象的地址发生了变化,说明深拷贝是将原对象复制了一份,且在堆中重新分配了内存。

prototype = org.xgllhz.designpattern.createtype.prototype.deep.Prototype@50c87b21 prototype.friend = 403716510
prototype1 = org.xgllhz.designpattern.createtype.prototype.deep.Prototype@5649fd9b prototype1.friend = 701141022
prototype2 = org.xgllhz.designpattern.createtype.prototype.deep.Prototype@2d928643 prototype2.friend = 112061925

7、原型模式的源码分析

  原型模式的本质是拷贝原来的对象从而创建新的对象。但当原对象存在引用数据类型时,若使用浅拷贝实现,则对新对象的修改会导致原对象也被修改;若使用深拷贝实现,则不会出现这种情况。

  JDK 中存在很多深拷贝的源码,以 java.util.ArrayList 为例:

public Object clone() {
  	try {
    		ArrayList<?> v = (ArrayList<?>) super.clone();
      	// 在这里对数组进行了拷贝 这个拷贝实际上是在堆中生成了一个新对象
    		v.elementData = Arrays.copyOf(elementData, size);
    		v.modCount = 0;
    		return v;
  	} catch (CloneNotSupportedException e) {
    		// this shouldn't happen, since we are Cloneable
    		throw new InternalError(e);
  	}
}

相关文章:

  • JVM之垃圾收集器三
  • 【web前端期末大作业】基于html关爱空巢老人网页设计与实现
  • flutter课程(The Complete 2021 Flutter Development Bootcamp with Dart)学习总结
  • 架构师知识体系梳理
  • LaTex使用技巧9:argmin / argmax下标写法
  • MySQL表的操作
  • 给定一个已排序的数组,使用就地算法将重复的数字移除,使数组中的每个元素只出现一次,返回新数组的长度
  • Vue2基础篇-01-Vue2 入门概述
  • CleanMyMac2023一键清除垃圾缓存和恶意广告插件 时刻保持Mac畅快运行
  • 通信原理 | 彻底搞懂卷积
  • 微服务框架 SpringCloud微服务架构 20 RestClient 操作索引库 20.5 删除和判断索引库
  • Vue3知识点之数据侦测
  • 【Python恶搞】Python实现祝福单身狗的恶搞项目,快@你的好朋友,祝福他吧 | 附源码
  • 共享车位|基于SpringBoot+vue+node共享车位平台的设计与实现
  • 【Android - 技术期刊】第004期
  • 一起用Go做一个小游戏(上)
  • HDU1074 Doing Homework(状压dp)
  • 前端面试题合集
  • Python均匀分布和三角形分布
  • IT行业面试技巧,90%的人都不知道
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉