克隆羊的原型模式你明白吗?

今天我们来学习24种设计模式中的第五种——原型模式,它主要用于创建重复的对象,同时又能保证系统的性能,下面我们就来具体看看这种设计模式的巧妙之处。

应用前景:

在我们应用程序中可能有某些对象的结构比较复杂,但是我们又需要频繁的使用它们,如果这个时候我们来不断的新建这个对象势必会大大损耗系统内存,这个时候我们需要使用原型模式来对这个结构复杂又要频繁使用的对象进行克隆。所以原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

原型模式的概念:

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。

原型模式的特点:

该模式的主要优点如下:

  • 性能提高。
  • 逃避构造函数的约束。

主要缺点如下:

  • 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
  • 必须实现 Cloneable 接口。
原型模式的主要角色:
  • 抽象原型类:规定了具体原型对象必须实现的接口。
  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
代码:

1.传统方式的克隆羊问题

总结:

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

2.原型模式下的克隆羊问题:

浅拷贝:

浅拷贝的介绍:

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象中的该成员变量的值。
  • 下面我们所讲的克隆羊其实就是浅拷贝,浅拷贝是使用默认的clone()方法来实现的,sheep = (Sheep) super.clone();

克隆羊原型类(Sheep)

package cn.ppdxzz.prototype.shallowclone;

/**
 * Description:克隆羊原型类
 *
 * @Date: 2020/3/7 19:31
 * @Author: PeiChen
 */
public class Sheep implements Cloneable {
    private String name;//姓名
    private int age;//年龄
    private String color;//颜色
    private String type = "蒙古羊";//种类

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

    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 String getColor() {
        return color;
    }

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

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

    //克隆该实例,使用默认的clone方法来完成
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return sheep;
    }
}

原型模式的克隆羊

package cn.ppdxzz.prototype.shallowclone;

/**
 * Description:原型模式的克隆羊
 *
 * @Date: 2020/3/7 19:42
 * @Author: PeiChen
 */
public class Client {

    public static void main(String[] args) {
        Sheep sheep = new Sheep("多利",1,"白色");
        Sheep sheep1 = (Sheep)sheep.clone();//克隆
        Sheep sheep2 = (Sheep)sheep.clone();//克隆
        Sheep sheep3 = (Sheep)sheep.clone();//克隆

        System.out.println("sheep1 = "+ sheep1);
        System.out.println("sheep2 = "+ sheep2);
        System.out.println("sheep3 = "+ sheep3);

    }
}

深拷贝:

深拷贝的介绍:

  • 复制对象的所有基本数据类型的成员变量值。
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。

深拷贝实现方式:

  • 方式一:重写clone方法来实现深拷贝。

  • 方式二:通过对象序列化实现深拷贝(推荐使用这种深拷贝方式)。

要进行深拷贝的一个对象

package cn.ppdxzz.prototype.deepclone;

import java.io.Serializable;
/**
 * Description:要进行深拷贝一个对象
 *
 * @Date: 2020/3/7 21:28
 * @Author: PeiChen
 */
public class DeepCloneClass implements Cloneable, Serializable {
    private String cloneName;//深拷贝对象的名称
    private String cloneDescription;//深拷贝对象的描述

    public DeepCloneClass() {
    }

    public DeepCloneClass(String cloneName, String cloneDescription) {
        this.cloneName = cloneName;
        this.cloneDescription = cloneDescription;
    }

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

深拷贝原型对象(包含引用数据类型)

package cn.ppdxzz.prototype.deepclone;

import java.io.*;

/**
 * Description:深拷贝原型对象(包含引用数据类型)
 *
 * @Date: 2020/3/8 11:12
 * @Author: PeiChen
 */
public class DeepPrototype implements Cloneable, Serializable {
    private String name;//String类型
    private DeepCloneClass deepCloneClass;//引用数据类型,要进行深拷贝的对象

    public String getName() {
        return name;
    }

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

    public DeepCloneClass getDeepCloneClass() {
        return deepCloneClass;
    }

    public void setDeepCloneClass(DeepCloneClass deepCloneClass) {
        this.deepCloneClass = deepCloneClass;
    }

    //方式一实现深拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //1.此处完成对基本数据类型以及String类型的拷贝
        deep = super.clone();
        //2.对引用数据类型的数据单独处理
        DeepPrototype deepPrototype = (DeepPrototype) deep;
        deepPrototype.setDeepCloneClass((DeepCloneClass)deepCloneClass.clone());

        return deepPrototype;
    }
    //方式二实现深拷贝
    public Object deepClone() {
        //1.创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {
            //2.序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            //3.反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepPrototype copyObj = (DeepPrototype) ois.readObject();
            return copyObj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        finally {
            //4.关闭流
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端验证是否可以进行深拷贝

package cn.ppdxzz.prototype.deepclone;

/**
 * Description:客户端
 *
 * @Date: 2020/3/8 11:31
 * @Author: PeiChen
 */
public class Client {
    public static void main(String[] args) throws Exception {
        DeepPrototype d = new DeepPrototype();
        d.setName("深拷贝");
        d.setDeepCloneClass(new DeepCloneClass("深拷贝对象","我是需要进行深拷贝的对象"));
        //1.方式一完成深拷贝
        DeepPrototype dClone = (DeepPrototype) d.clone();
        System.out.println("原型.name = " + d.getName() + " 原型.deepCloneClass = " + d.getDeepCloneClass().hashCode());
        System.out.println("克隆.name = " + dClone.getName() + " 克隆.deepCloneClass = " + dClone.getDeepCloneClass().hashCode());
        //2.方式二完成深拷贝
        DeepPrototype dClone = (DeepPrototype)d.deepClone();
        System.out.println("原型.name = " + d.getName() + " 原型.deepCloneClass = " + d.getDeepCloneClass().hashCode());
        System.out.println("克隆.name = " + dClone.getName() + " 克隆.deepCloneClass = " + dClone.getDeepCloneClass().hashCode());
    }
}

深拷贝结果演示:

原型模式源码分析:

Spring中原型bean的创建,就是使用的原型模式 。

总结:
  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
  • 不用重新初始化对象,而是动态地获得对象运行时的状态。
  • 如果原始对象发生变化(增加或者减少属性),其它克隆的对象也会发生相应的变化,无需修改代码。
  • 在实现深克隆的时候可能需要比较复杂的代码。
  • 注意:它需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了OCP原则。

至此,创建型的五种设计模式都已讲完,它们分别是单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。下期我们就开始进入到结构型的设计模式的学习之中,敬请期待。

总结:
  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
  • 不用重新初始化对象,而是动态地获得对象运行时的状态。
  • 如果原始对象发生变化(增加或者减少属性),其它克隆的对象也会发生相应的变化,无需修改代码。
  • 在实现深克隆的时候可能需要比较复杂的代码。
  • 注意:它需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了OCP原则。

至此,创建型的五种设计模式都已讲完,它们分别是单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。下期我们就开始进入到结构型的设计模式的学习之中,敬请期待。

微信公众号,JavaAnything,期待你的关注!
微信交流群:
在这里插入图片描述

发布了17 篇原创文章 · 获赞 18 · 访问量 1212

猜你喜欢

转载自blog.csdn.net/active_pig/article/details/105613664