原型设计模式

原型模式

视频来源 原型模式 46-49

概述

原型模式(Prototype Pattern):使用原型模式指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建模式。

需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。

角色

原型模式包含如下角色:

  • 抽象原型类:规定了具体原型对象必须实现的clone方法,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
  • 具体原型类:它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
  • 访问类:使用具体原型类中的clone方法来复制新的对象。

Java语言提供了clone方法

所有的Java类都继承自 java.lang.Object,Object 类提供一个 clone() 方法,可以将一个Java对象复制一份。因此在Java中可以直接使用 Object 提供的 clone() 方法来实现对象的克隆,Java语言中的原型模式实现很简单。

需要注意的是能够实现克隆的Java类必须实现一个 标识接口 Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个 CloneNotSupportedException 异常。

实现

原型模式的克隆分为浅克隆和深克隆。

浅克隆: 创建一个新对象,新对象的属性和原对象完全相同,对于非基本类型属性,仍指向原有属性指向的对象的内存地址。

深克隆创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

浅克隆有点像 快捷方式 , 深克隆有点像复制文件

具体原型类:

public class Realizetype implements Cloneable {

    public Realizetype() {
        System.out.println("具体的原型对象创建完成!");
    }

    @Override
    protected Realizetype clone() throws CloneNotSupportedException {

        Realizetype realizetype = null;
        realizetype =  (Realizetype)super.clone()

        System.out.println("具体原型复制成功!");
        return realizetype;
    }

}

客户端测试访问类

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Realizetype r1 = new Realizetype(); // 原型对象
        Realizetype r2 = r1.clone(); // 克隆出来的对象
        System.out.println("对象r1和r2是同一个对象?" + (r1 == r2));

        /**
         * 具体的原型对象创建完成!
         * 具体原型复制成功!
         * 对象r1和r2是同一个对象?false
         */
    }
}

运行结果:clone 原型对象的时候,不会调用构造方法,因为该对象不是通过 new 创建的

查看Object 中的clone方法

    protected native Object clone() throws CloneNotSupportedException;

案例

【例】使用原型模式生成 “三好学生” 奖状。

代码如下:

/**
 * 奖状类
 */
@Data
public class Citation implements Cloneable {
    private String name; // 奖状上的名字

    public String getName() {
        return name;
    }

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

    public void show() {
        System.out.println(name + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}
/**
 * 测出访问类
 */
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation c1 = new Citation();
        c1.setName("张三");

        // 复制奖状
        Citation c2 = c1.clone();
        c2.setName("李四"); // 将奖状的名字改为李四

        c1.show();
        c2.show();
    }
}

使用场景

以下两种情况,可以使用原型模式快捷的创建对象:

  • 对象的创建非常复杂
  • 性能和安全要求比较高。

深克隆

引用对象的浅克隆

将上面的 “三好学生” 奖状的案例中 Citation 类的 name 属性修改为 Student 类型的属性。

/**
 * 学生类
 */
@Data
public class Student {
    private String name;
    public Student(String name) {
        this.name = name;
    }
}

/**
 * 奖状类
 */
@Data
public class Citation implements Cloneable {
    private Student stu; // 这是个引用对象

    void show() {
        System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

public class CitationTest {
    public static void main(String[] args) throws Exception {
        // 原型对象
        Citation c1 = new Citation();
        Student stu1 = new Student("张三");
        c1.setStu(stu1);

        // 复制奖状并修改名字
        Citation c2 = c1.deepClone();
        Student stu2 = c2.getStu();
        stu2.setName("李四");

        // 判断stu1对象和stu2对象是否是同一个对象
        System.out.println("st1和stu2是否同一个对象?" + (stu1 == stu2));

        c1.show();
        c2.show();
    }
}

运行结果:stu1 和 stu2 是同一个对象

st1和stu2是否同一个对象?true
李四同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!
李四同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!

stu1 和 stu2 是同一个对象,就会产生将 stu1 中 name 属性值改为 “李四”,两个 Citation 对象中显示的都是李四。

这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制。

这种情况需要使用深克隆,而进行深克隆需要使用对象流。

深克隆实现1.文件流 + 对象流

学生类(Student)同上,但是要实现 Serializable 接口。

奖状类(Citation)实现 Serializable 接口,定义一个 deepClone 方法,通过文件输出流流和对象流实现深克隆。

// 1 所有对象都实现序列化的接口
// 2 自定义一个深度克隆方法deepClone, 通过文件流和对象流的方式实现对象的深度拷贝
public Citation deepClone() throws Exception {
  try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
       ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));) {
    oos.writeObject(this);
    Citation citation = (Citation) ois.readObject();
    return citation;
  }
}

实现 2:字节数组流 + 对象流

学生类(Student)同上,实现 Serializable 接口。

奖状类(Citation)实现 Serializable 接口,定义一个 deepClone 方法,通过字节数组流和对象流实现深克隆。

// 1 所有对象都实现序列化的接口
// 2 自定义一个深度克隆方法deepClone, 通过字节数组流和对象流的方式实现对象的深度拷贝
public Citation deepClone2() throws Exception {
  try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
       ObjectOutputStream oos = new ObjectOutputStream(bos)) {
    oos.writeObject(this);
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    return (Citation) ois.readObject();
  }
}

实现3:具体原型类中的引用类型进行克隆

public class Student implements Cloneable{
    private String name;
    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    //引用类型进行clone
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
    @Override
    public Citation clone() throws CloneNotSupportedException {
        Citation citation =  (Citation) super.clone();
        citation.setStu((Student)stu.clone());
        return citation;
    }

运行结果:

st1和stu2是否同一个对象?false
张三同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!
李四同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!

原型模式在Spring中的应用场景

在Spring中,用户也可以采用原型模式来创建新的Bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。

这里的原型模式,也就是常说的Spring中的多实例模式,Spring中还有大家熟知的单实例模式,即Sigleton

参考链接:https://blog.csdn.net/m0_53157173/article/details/120015362

参考链接:https://blog.csdn.net/weixin_43734095/article/details/122339858

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值