原型模式详解

原型模式

1.1原型模式概述

1.1.1原型模式定义

原型模式(Prototype Pattern)指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,属于创建型设计模式。
原型模式的核心在于复制原型对象。以系统中已存在的一个对象为原型,基于二进制流进行复制,不需要经历对象的初始化过程,提高了性能。

1.1.2原型模式的适用场景

  • 适用于需要创建大对象,或者对象初始化非常耗时,占用太多资源,可以考虑使用原型模式
  • 系统中大量使用该类对象,且调用者需要重新赋值的重复性操作过多。

1.1.4原型模式的通用写法

public interface IPrototype {
    T clone();
}
public class ConcreatePrototypeA implements IPrototype{

    private String desc;

    public ConcreatePrototypeA(String desc) {
        this.desc = desc;
    }

    @Override
    public ConcreatePrototypeA clone() {
        return new ConcreatePrototypeA(this.desc);
    }

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

1.2深克隆与浅克隆

1.2.1浅克隆分析

在JAVA里,我们不需要创建原型接口,JAVA内置的原型接口—Cloneable接口,自定义的类只需要实现该接口并重写Object.clone()方法即可完成本类的复制。
这块需要注意,点开Cloneable接口查看源码:

/**
 * A class implements the Cloneable interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.

 *
 * Invoking Object's clone method on an instance that does not implement the
 * Cloneable interface results in the exception
 * CloneNotSupportedException being thrown.

 *
 * By convention, classes that implement this interface should override
 * Object.clone (which is protected) with a public method.

 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.

 *
 * Note that this interface does not contain the clone method.

 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.

 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

可以看到Cloneable其实是空接口,而clone方法其实属于Object的方法。
翻译Cloneable接口的注释我们就明白了,Cloneable其实就是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功,
如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。
接下来,做个小测试:

package org.example.prototype;

import lombok.Data;

import java.util.List;

@Data
public class Person implements Cloneable{
    private int age;
    private String name;
    private List hobbies;

    public Person clone(){
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
package org.example.prototype;

import java.util.ArrayList;
import java.util.List;

public class PrototypeTest {
    public static void main(String[] args) {
        Person person = new Person();
        person.setAge(18);
        person.setName("yml");
        List hobbies = new ArrayList<>();
        hobbies.add("篮球");
        hobbies.add("唱跳rap");
        person.setHobbies(hobbies);
        System.out.println(person);
        Person clone = person.clone();
        clone.getHobbies().add("游泳");
        System.out.println(person);
    }
}

上边代码使用super.clone给Person对象进行克隆,当我们改变克隆对象的属性时,得到的结果:

原型模式详解
以上结果说明,原型对象和克隆对象的引用对象属性指向同一对象的引用,意味着这种方式克隆的不是值而是引用的地址。这个显然不符合我们的预期,因为我们希望克隆对象和原型对象是两个独立的对象,互不影响。
Java自带的克隆方式为浅克隆。而如果我们想进行深克隆,可以浅克隆之后,手动给克隆对象的相关属性分配内存,不过这种方式太重复且繁琐,在Java中,一般使用序列化和反序列化实现深克隆。

1.2.2使用序列化实现深克隆

package org.example.prototype;

import lombok.Data;

import java.io.*;
import java.util.List;

@Data
public class Person implements Cloneable,Serializable{
    private int age;
    private String name;
    private List hobbies;

    public Person clone(){
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public Person deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream stream = new ObjectOutputStream(bos);
        stream.writeObject(this);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        return (Person) objectInputStream.readObject();
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
public class PrototypeTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person();
        person.setAge(18);
        person.setName("yml");
        List hobbies = new ArrayList<>();
        hobbies.add("篮球");
        hobbies.add("唱跳rap");
        person.setHobbies(hobbies);
        System.out.println(person);
        Person deepClone = person.deepClone();
        deepClone.getHobbies().add("游泳");
        System.out.println(person);
    }
}

原型模式详解
从运行结果看来,我们的确实现了深克隆。

1.2.3原型模式在源码中的应用

  • JDK中ArrayList源码
 /**
     * Returns a shallow copy of this ArrayList instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this ArrayList instance
     */
    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);
        }
    }

其实ArrayList是对所有元素遍历一遍,这种也是实现深克隆的一种方式,但是这种相当于硬编码,更推荐序列化方式实现深克隆。

Original: https://www.cnblogs.com/amazing-yml/p/15961606.html
Author: amazing_yml
Title: 原型模式详解

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/644537/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球