设计模式学习05(Java实现)——原型模式

写在前面

  • 记录学习设计模式的笔记
  • 提高对设计模式的灵活运用

学习地址

https://www.bilibili.com/video/BV1G4411c7N4

https://www.bilibili.com/video/BV1Np4y1z7BU

参考文章

http://c.biancheng.net/view/1317.html

项目源码
https://gitee.com/zhuang-kang/DesignPattern

6,原型模式

6.1原形模式的定义和特点

原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。

原型模式的优点:

  • Java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

原型模式的缺点:

  • 需要为每一个类都配置一个 clone 方法
  • clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

6.2 原型模式的结构与实现

6.2.1 原形模式的结构

原型模式包含以下主要角色。

  1. 抽象原型类:规定了具体原型对象必须实现的接口。
  2. 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  3. 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

6.2.2 代码实现

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

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

6.2.2.1 浅拷贝

IdCard

package com.zhuang.prototype.shallowclone;

/**
 * @Classname IdCard
 * @Description 浅拷贝的示例
 * @Date 2021/3/19 12:16
 * @Created by dell
 */

public class IdCard {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "id=" + id +
                '}';
    }
}

Person

package com.zhuang.prototype.shallowclone;

/**
 * @Classname Person
 * @Description 浅拷贝的示例
 * @Date 2021/3/19 12:17
 * @Created by dell
 */

public class Person implements Cloneable {

    private String name;
    private int age;
    private IdCard idCard;

    public Person() {
    }

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", idCard=" + idCard + ", idCard.hashCode=" + idCard.hashCode() +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

PersonTest

package com.zhuang.prototype.shallowclone;

/**
 * @Classname PersonTest
 * @Description 浅拷贝测试类
 * @Date 2021/3/19 12:17
 * @Created by dell
 */

public class PersonTest {
    public static void main(String[] args) throws Exception {
        Person person = new Person("张三", 20, new IdCard("10086"));

        Person person1 = (Person) person.clone();
        Person person2 = (Person) person.clone();

        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);

    }
}
image

我们发现可以通过实现implements Cloneable来完成浅拷贝,基本变量是值传递克隆,而引用对象IdCard则是引用传递,这不符合我们面向对象思想,每一个Person应该都有一个独立的IdCard,而不是共用一个,而要解决这种问题,我们需要使用深克隆

6.2.2.2 深拷贝(第一种)

IdCard

package com.zhuang.prototype.deepclone.one;

/**
 * @Classname IdCard
 * @Description 深克隆的示例
 * @Date 2021/3/19 12:33
 * @Created by dell
 */
//实现Cloneable接口
public class IdCard implements Cloneable {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "IdCard{" +
                "id='" + id + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Person

package com.zhuang.prototype.deepclone.one;

/**
 * @Classname Person
 * @Description 深克隆的示例
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class Person implements Cloneable {
    private String name;
    private int age;
    private IdCard idCard;

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Person{" +
                "personHashCode=" + this.hashCode() +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", idCard=" + idCard +
                ", idCardHashCode=" + idCard.hashCode() +
                '}';
    }

    //深克隆需要自己手动实现,在对象引用中也要实现clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //完成基本数据类型的拷贝
        //通过new关键字创建的对象是引用类型

        Object person = super.clone();

        //对引用类型单独处理
        Person p = (Person) person;
        IdCard idCard = (IdCard) p.getIdCard().clone(); //实现自己的克隆
        p.setIdCard(idCard);
        return p;
    }
}

PersonTest

package com.zhuang.prototype.deepclone.one;

import java.io.Serializable;

/**
 * @Classname PersonTest
 * @Description 深克隆测试类
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class PersonTest implements Serializable {
    public static void main(String[] args) throws Exception {

        Person person = new Person("张三", 20, new IdCard("10086"));

        Person person1 = (Person) person.clone();
        Person person2 = (Person) person.clone();

        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);

    }
}
image

使用这种深克隆的方式,完美的解决了当数据类型为引用类型时,只是拷贝原引用对象地址而不是一个全新的引用对象的引用,但是这种实现有一个很大的弊端,需要在每一个对象中都实现clone方法,如果类全是你自己写的,那自然没问题,实现一下就行了,不过有点麻烦。但是,如果你引用的是第三方的一个类,无法修改源代码,这种方式,显然就无法实现深克隆了

6.2.2.2 深拷贝(第二种)

IdCard

package com.zhuang.prototype.deepclone.two;

/**
 * @Classname IdCard
 * @Description 深克隆的示例2
 * @Date 2021/3/19 12:33
 * @Created by dell
 */
//实现Serializable接口
public class IdCard implements Serializable {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "IdCard{" +
                "id='" + id + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Person

package com.zhuang.prototype.deepclone.two;

import java.io.*;

/**
 * @Classname Person
 * @Description 深克隆的示例2
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class Person implements Serializable {
    private String name;
    private int age;
    private IdCard idCard;

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Person{" +
                "personHashCode=" + this.hashCode() +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", idCard=" + idCard +
                ", idCardHashCode=" + idCard.hashCode() +
                '}';
    }

    //序列化的方式
    public Person deelClone() {
        //创建流对象
        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 (Person) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                ois.close();
                bis.close();
                oos.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

PersonTest

package com.zhuang.prototype.deepclone.two;

/**
 * @Classname PersonTest
 * @Description 深克隆测试类
 * @Date 2021/3/19 12:33
 * @Created by dell
 */

public class PersonTest {
    public static void main(String[] args) throws Exception {
        //创建一个对象
        Person person = new Person("张三", 20, new IdCard("10086"));

        //克隆两个对象
        Person person1 = (Person) person.deelClone();
        Person person2 = (Person) person.deelClone();
        System.out.println("深拷贝(第二种 实现序列化接口)");
        //打印三人信息
        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);

    }
}
image

这种方式我们需要手动编写deepClone方法,使用Java流中的序列化与反序列化来实现深克隆,但是这种实现,需要在每一个类中都继承序列化Serializable接口,这种方式,如果你调用的是第三方类,也有可能第三方类上没有实现Serializable序列化接口,但是一般来说,大多都会实现,总的来说,这种比较推荐使用,而且效率也高

6.3 原型模式的应用场景

  • 对象之间相同或相似,即只是个别的几个属性不同的时候。
  • 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  • 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  • 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
  • 在 Spring中,原型模式应用的非常广泛,例如 scope='prototype'、JSON.parseObject() 等都是原型模式的具体应用。

6.4 原型模式的注意事项和细节

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能提高效率
  • 不用重新初始化对象,动态地获得对象运行时的状态
  • 如果原始对象发生变化(增加或减少属性),其他克隆对象也会发生相应的变化,无需修改代码

写在最后

  • 如果我的文章对你有用,请给我点个,感谢你!
  • 有问题,欢迎在评论区指出!

你可能感兴趣的:(设计模式学习05(Java实现)——原型模式)