设计模式-创建型-原型模式

发布时间 2023-09-01 13:49:17作者: JavaCoderPan
title: 设计模式-创建型-原型模式
keywords: 设计模式
cover: [https://s1.ax1x.com/2023/08/31/pP01Vit.png]
# sticky: 10
banner:  
  type: img
  bgurl: https://s1.ax1x.com/2023/08/31/pP01Vit.png
  bannerText: 设计模式-创建型-原型模式
categories: 设计模式
tags:
  - 设计模式
toc: false # 无需显示目录

原型模式

原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象

原型模式适用场景:

  • 类初始化消耗资源较多

  • 使用new生成一个对象需要非常繁琐的过程(数据准备、访问权限)

  • 构造函数比较复杂

  • 在循环体中产生大量对象

1、浅克隆

一个标准的原型模式应该这样设计,首先创建一个原型Prototype接口

/**
 * 原型Prototype接口
 * @author ss_419
 */
public interface Prototype {
    Prototype clone();
}

创建具体需要克隆的类ConcretePrototypeA:

/**
 * TODO 具体需要克隆的类
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/9/1 11:02
 */
public class ConcretePrototypeA implements Prototype{
    private int age;
    private String name;
    private List hobbies;



    /**
     * 实现Prototype原型类的clone方法
     * 注意:这里的克隆是浅克隆,对引用对象克隆的是地址,而不是全新的值
     * @return
     */
    @Override
    public ConcretePrototypeA clone() {
        // 原型模式:指定原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        // 设置该对象原型类的属性
        concretePrototype.setAge(this.getAge());
        concretePrototype.setName(this.getName());
        concretePrototype.setHobbies(this.getHobbies());
        // 返回复制实例
        return concretePrototype;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public List getHobbies() {
        return hobbies;
    }

    public void setHobbies(List hobbies) {
        this.hobbies = hobbies;
    }
}

创建Client类:

/**
 * TODO 在客户端类中进行克隆操作-生成对象
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/9/1 11:18
 */
public class Client {
    // 注入原型对象
    private Prototype prototype;

    public Client(Prototype prototype) {
        this.prototype = prototype;
    }

    /**
     * 开始克隆,该方法只需指定克隆对象的类型即可
     * @return
     */
    public Prototype startClone(Prototype concretePrototype){
        return (Prototype)concretePrototype.clone();

    }
}

测试代码:

public class PrototypeTest {
    public static void main(String[] args) {
        // 创建一个具体的需要克隆的对象
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        // 填充属性,方便测试
        concretePrototype.setAge(19);
        concretePrototype.setName("ukyo");
        List hobbies = new ArrayList<String>();
        hobbies.add("xxxx1");
        hobbies.add("xxxx2");
        hobbies.add("xxxx3");
        concretePrototype.setHobbies(hobbies);
        System.out.println("concretePrototype = " + concretePrototype);

        // 创建client对象,准备开始克隆s
        Client client = new Client(concretePrototype);
        ConcretePrototypeA clone =
                   // 这里的克隆仅仅对当前对象的值进行的复制
                (ConcretePrototypeA) client.startClone(concretePrototype);
        System.out.println("clone = " + clone);

        /***
         * 因为是浅拷贝,所以对象中的引用地址没有重新分配内存空间,就算进行了克隆,它们还是指向同一块内存空间
         * 可以看出,hobbies的引用地址是相同的,意味着复制的不是值,而是引用的地址
         *
         * 浅克隆只是完整复制了值类型数据,没有赋值引用对象
         */
        System.out.println("克隆对象中的引用类型地址:" + clone.getHobbies());
        System.out.println("原对象中的引用类型地址:" + concretePrototype.getHobbies());
        System.out.println("对象地址比较 => "+(concretePrototype.getHobbies() == clone.getHobbies()));
    }
}

2、深拷贝

/**
 * TODO 原型猴子类Monkey
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/9/1 11:28
 */
public class Monkey {
    public int height;
    public int weight;
    public Date birthday;
}
public class JinGuBang implements Serializable {
    public float h = 100;
    public float d = 10;

    public void big(){
        this.d *= 2;
        this.h *= 2;
    }

    public void small(){
        this.d /= 2;
        this.h /= 2;
    }
    
}
public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {

    public JinGuBang jinGuBang;

    public QiTianDaSheng() {
        // 只是初始化
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {

        return this.deepClone();
    }

    /**
     * 创建深拷贝方法
     */
    public Object deepClone() {
        try {
            // 创建字节输出流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            // 读取输入流中的对象,并强转
            QiTianDaSheng copy = (QiTianDaSheng) ois.readObject();// 生成一个全新的copy对象
            copy.birthday = new Date();
            return copy;
        } catch (Exception e) {
            e.printStackTrace();
            // 克隆失败返回null
            return null;
        }
    }

    /**
     * 浅拷贝
     *
     */
    public QiTianDaSheng shallowClone(QiTianDaSheng target){
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        // 从目标对象中拷贝值
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.weight;
        // 拷贝引用对象,这里很明显可以看出指向同一块内存空间
        qiTianDaSheng.jinGuBang = target.jinGuBang;

        qiTianDaSheng.birthday = new Date();
        return qiTianDaSheng;
    }

}

深拷贝测试:

/**
 * TODO 深拷贝测试
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/9/1 11:39
 */
public class DeepTest {
    public static void main(String[] args) {
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        // 深拷贝
        try{
            QiTianDaSheng clone =
                    (QiTianDaSheng) qiTianDaSheng.clone();
            System.out.println("通过序列化操作进行深拷贝 => "+ (clone.jinGuBang == qiTianDaSheng.jinGuBang));
        }catch (Exception e){
            e.printStackTrace();
        }

        // 浅拷贝
        QiTianDaSheng q = new QiTianDaSheng();
        QiTianDaSheng n = q.shallowClone(q);
        System.out.println("浅克隆 => "+ (q.jinGuBang == n.jinGuBang));
    }
}