目录

一、原型模式是什么?

二、传统方式案例实现

1.类图

2.代码实现

3.总结

三、使用原型模式案例实现

1.类图

2.代码实现

3.总结 

四、原型模式在Spring 框架中的使用

五、原型模式中深拷贝和浅拷贝

六、总结

一、原型模式是什么?

基本介绍

  • 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
  • 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象无需知道如何创建的细节
  • 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
  • 形象的理解:孙大圣拔出猴毛,变出其它孙大圣

类图

设计模式-原型模式-原理结构图

二、传统方式案例实现

需求:

现在有一只羊tom,姓名为:tom,年龄为:1,颜色为:白色,请编写程序创建和tom羊 属性完全相同的10只羊。

1.类图

2.代码实现

羊类(需要创建10份)

/**
 * 羊类🐏
 */
public class Sheep {
    private String name;
    private Integer age;
    private String color;

    public Sheep(String name, Integer age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public Sheep() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}


客户端 依赖 羊类
public class ClientTest {
    public static void main(String[] args) {
        //创建一只羊 名称是 tom 年龄1岁 白色的 复制10只一样的
        Sheep sheep = new Sheep("tom", 1, "白色");

        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep6 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep7 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep8 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep9 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep10 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());

        System.out.println("sheep = " + sheep);
        System.out.println("sheep2 = " + sheep2);
        System.out.println("sheep3 = " + sheep3);
        System.out.println("sheep4 = " + sheep4);
        System.out.println("sheep5 = " + sheep5);
        System.out.println("sheep6 = " + sheep6);
        System.out.println("sheep7 = " + sheep7);
        System.out.println("sheep8 = " + sheep8);
        System.out.println("sheep9 = " + sheep9);
        System.out.println("sheep10 = " + sheep10);

    }
}

效果:

3.总结

传统的方式的优缺点

  1. 优点是比较好理解,简单易操作
  2. 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
  3. 总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
  4. 改进的思路分析

思路:Java中Object类是所有类的根类,0biect类提供了一个cone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力=>原型模式

三、使用原型模式案例实现

1.类图

2.代码实现

原型类

/**
 * 羊类
 */
public class Sheep implements Cloneable{

    private String name;
    private Integer age;
    private String color;

    public Sheep(String name, Integer age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public Sheep() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }

    //克隆该实例 使用默认的clone 方法来完成
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


客户端

public class ClientTest {

    public static void main(String[] args) throws Exception {
        System.out.println("原型模式完成对象的创建");
        Sheep sheep = new Sheep("tom", 18, "彩虹色");
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        Sheep sheep4 = (Sheep) sheep.clone();
        Sheep sheep5 = (Sheep) sheep.clone();

        System.out.println("sheep = " + sheep + "\thashCode=" + sheep.hashCode());
        System.out.println("sheep1 = " + sheep1 + "\thashCode=" + sheep1.hashCode());
        System.out.println("sheep2 = " + sheep2 + "\thashCode=" + sheep2.hashCode());
        System.out.println("sheep3 = " + sheep3 + "\thashCode=" + sheep3.hashCode());
        System.out.println("sheep4 = " + sheep4 + "\thashCode=" + sheep4.hashCode());
        System.out.println("sheep5 = " + sheep5 + "\thashCode=" + sheep5.hashCode());
    }
}


效果:

3.总结 

使用原型模式改进传统方式,让程序具有更高的效率和拓展性

拓展性: 如果羊类添加了一个基本的属性参数,使用clone方法创建的对象不需要修改代码,直接new则是需要修改代码

更高的效率性:

  1. 对象初始化开销

    1. 使用new关键字创建对象时,Java会分配内存并调用对象的构造函数来初始化这个新对象。

    2. 使用clone()方法时,Java会创建一个已经存在的对象的浅拷贝(或深拷贝,取决于实现)。这意味着新对象可以复用原始对象中的很多已经初始化好的状态,避免了重复初始化。

  2. 内存分配效率

    • 在某些JVM实现中,clone()方法可能会利用一些优化手段来更高效地分配内存。例如,JVM可能能够预测到克隆操作并预先分配内存块,从而减少内存分配的开销。

四、原型模式在Spring 框架中的使用

1.代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--原型模式创建bean-->
    <bean id="id01" class="com.good.app.prototype.spring.Monster" scope="prototype"/>
</beans>
交给spring管理的类

public class Monster {
    private Integer id = 10;
    private String name = "牛魔王";
    private String skill = "芭蕉扇";

    public Monster() {
        System.out.println("Monster 创建");
    }

    public Monster(Integer id, String name, String skill) {
        this.id = id;
        this.name = name;
        this.skill = skill;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }

    @Override
    public String toString() {
        return "Monster{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", skill='" + skill + '\'' +
                '}';
    }
}

测试类:

public class SpringTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Object id01 = context.getBean("id01");
        Object id02 = context.getBean("id01");
        System.out.println("id01 = " + id01);
        System.out.println("id02 = " + id02);
        System.out.println("id01==id02 = " + id01 == id02);
    }
}


效果:

2.源码跟踪:

3.总结:

在Spring框架中,虽然默认行为是以单例模式(Singleton)创建和管理Bean,但它也支持原型作用域(Prototype Scope)。当在Spring配置文件中将Bean的作用域设置为prototype时,Spring容器对每个getBean()请求都会创建一个新的Bean实例。这意味着如果你多次请求同一个Bean,Spring会每次都创建一个新的实例,而不是返回同一个共享的实例。

五、原型模式中深拷贝和浅拷贝

1.浅拷贝定义

浅拷贝是指创建一个新对象,这个新对象的属性值和原对象的属性值相同。但是,如果属性值是引用类型(如对象、数组等),则浅拷贝后的新对象仍然引用原对象中的内存地址。换句话说,浅拷贝只复制了对象本身和值类型属性,而没有复制引用类型属性所指向的对象

2.浅拷贝特点

  • 实现简单:只需复制对象本身和值类型属性。

  • 节省内存:由于引用类型属性没有复制,因此节省了内存空间。

  • 可能导致问题:如果原对象中的引用类型属性被修改,那么浅拷贝后的新对象中的对应属性也会受到影响,因为它们引用的是同一个对象。

3.深拷贝定义:

深拷贝是指创建一个新对象,并且复制原对象中的所有属性,包括值类型属性和引用类型属性。对于引用类型属性,深拷贝会创建一个新的对象来存储这些属性的值,而不是简单地复制引用。

4.深拷贝特点:

  • 实现复杂:需要递归地复制所有引用类型属性所指向的对象。

  • 占用更多内存:由于复制了所有引用类型属性所指向的对象,因此会占用更多的内存空间。

  • 安全性高:深拷贝后的新对象与原对象完全独立,互不影响。即使原对象中的属性被修改,也不会影响深拷贝后的新对象。

5.实现方式:

  1. 重写clone()方法:对于需要深拷贝的对象及其所有引用类型属性所指向的对象,都需要实现Cloneable接口并重写clone()方法。在重写clone()方法时,需要递归地调用引用类型属性的clone()方法来实现深拷贝。

  2. 序列化与反序列化(推荐:另一种实现深拷贝的方法是使用序列化与反序列化。将原对象序列化为字节流,然后再将字节流反序列化为一个新的对象。这种方法不需要手动重写clone()方法,但需要确保所有引用类型属性所指向的对象都实现了Serializable接口。

6.代码:



//只包含基础数据类和String 类型的类 用来进行应用
@ToString
public class DeepCloneableTarget implements Serializable, Cloneable {

    @Serial
    private static final long serialVersionUID = 1L;

    private String cloneName;

    private String cloneClass;

    public DeepCloneableTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

    public DeepCloneableTarget() {
    }


    public String getCloneName() {
        return cloneName;
    }

    public void setCloneName(String cloneName) {
        this.cloneName = cloneName;
    }

    public String getCloneClass() {
        return cloneClass;
    }

    public void setCloneClass(String cloneClass) {
        this.cloneClass = cloneClass;
    }

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


}


//深度clone 测试
@ToString
public class DeepProtoType implements Serializable, Cloneable {


    @Serial
    private static final long serialVersionUID = 1L;
    private String name; //String 数据类型

    private DeepCloneableTarget deepCloneableTarget;//引用数据类型

    public DeepProtoType(String name, DeepCloneableTarget deepCloneableTarget) {
        this.name = name;
        this.deepCloneableTarget = deepCloneableTarget;
    }

    public DeepProtoType() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public DeepCloneableTarget getDeepCloneableTarget() {
        return deepCloneableTarget;
    }

    public void setDeepCloneableTarget(DeepCloneableTarget deepCloneableTarget) {
        this.deepCloneableTarget = deepCloneableTarget;
    }

    //深拷贝-方式1  使用clone方法(重写) 弊端 如果有很多引用类 需要每个都单独处理 比较麻烦
    @Override
    public DeepProtoType clone() {

        DeepProtoType deepProtoType = null;

        try {
            //处理基础数据类型和String类型的clone
            deepProtoType = (DeepProtoType) super.clone();

            //处理引用数据类型的clone
            deepProtoType.deepCloneableTarget = deepCloneableTarget.clone();
            return deepProtoType;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    //深拷贝-方式2 使用序列化的方式拷贝
    public DeepProtoType deepClone() {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this); //当前对象以对象流的方式输出

            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);

            return (DeepProtoType) ois.readObject();

        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {

            try {
                if (bos != null) {
                    bos.close();
                }
                if (oos != null) {
                    oos.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
        }

    }

}

//测试客户端类
public class DeepCloneTest {
    public static void main(String[] args) {
        System.out.println("使用重写clone方法进行深拷贝----------------------------------------------------");

        //深度clone测试
        DeepCloneableTarget target = new DeepCloneableTarget("张三", "李四");

        DeepProtoType deepProtoType = new DeepProtoType("王五", target);
        DeepProtoType clone1 = deepProtoType.clone();
        DeepProtoType clone2 = deepProtoType.clone();

        System.out.println("deepProtoType = " + deepProtoType + "  deepProtoType.deepCloneableTarget =" + deepProtoType.getDeepCloneableTarget().hashCode());
        System.out.println("clone1 = " + clone1 + "   clone1.deepCloneableTarget =" + clone1.getDeepCloneableTarget().hashCode());
        System.out.println("clone2 = " + clone2 + "   clone2.deepCloneableTarget =" + clone2.getDeepCloneableTarget().hashCode());


        System.out.println("使用序列化方法进行深拷贝----------------------------------------------------");
        DeepProtoType clone3 = deepProtoType.deepClone();
        DeepProtoType clone4 = deepProtoType.deepClone();
        System.out.println("deepProtoType = " + deepProtoType + "  deepProtoType.deepCloneableTarget =" + deepProtoType.getDeepCloneableTarget().hashCode());
        System.out.println("clone3 = " + clone3 + "   clone3.deepCloneableTarget =" + clone3.getDeepCloneableTarget().hashCode());
        System.out.println("clone4 = " + clone4 + "   clone4.deepCloneableTarget =" + clone4.getDeepCloneableTarget().hashCode());
    }
}

运行结果:


六、总结

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率

  • 不用重新初始化对象,而是动态地获得对象运行时的状态

  • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的编号无需修改代码

  • 在实现深克隆的时候可能需要比较复杂的代码

  • 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点需要注意.

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐