创建型(四) - 原型模式

一、概念

原型模式(Prototype Pattern):利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。

使用场景:如果对象的创建成本比较大,而且同一个类的不同对象之间差别不大(大部分字段都相同),这种情况下可以考虑原型模式。

二、实现

原型模式有两种实现方法,深拷贝和浅拷贝。

  • 浅拷贝:只会复制对象中基本数据类型数据和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象。
  • 深拷贝:得到的是一份完完全全独立新的对象。

当一个类中只有基本数据类型时,浅拷贝与深拷贝是同样的。

当一个类中含有引用数据类型是,浅拷贝只是拷贝一份引用,修改浅拷贝的值,原来的也会跟着变化。

举个例子:

肯德基套餐A对象,包含一个可乐对象,一个汉堡包对象。

浅拷贝:就是复制了一份肯德基套餐A,但是里面包含的可乐和汉堡还是原来的那一份,如果咬一口汉堡,那么原来的那个就缺一块。

深拷贝:也是复制了一份肯德基套餐A,但是里面的可乐和汉堡是新的对象,如果咬一口汉堡,那么原来的那个还是完好无损的。

  • 浅拷贝实现:

代码:注意如果想实现克隆功能,要克隆的类要实现Cloneable 接口。

1、肯德基套餐A

java 复制代码
public class KFCSetMenuA implements Cloneable {
    private float price;
    private Cola cola;
    private Hamburger hamburger;

    ....省略set和get方法

    @Override
    protected Object clone() {
        KFCSetMenuA kfcSetMenuA = null;

        try {
            kfcSetMenuA = (KFCSetMenuA) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }
        return kfcSetMenuA;
    }
}

2、其他对象

java 复制代码
public class Cola implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

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

public class Hamburger implements Cloneable {

}

3、实现浅拷贝

java 复制代码
    public static void main(String[] args) throws CloneNotSupportedException {
        KFCSetMenuA kfcSetMenuA = new KFCSetMenuA();
        kfcSetMenuA.setPrice(15.5f);
        Cola cola = new Cola();
        cola.setName("可口可乐");
        kfcSetMenuA.setCola(cola);

        KFCSetMenuA cloneKFCSetMenuA = (KFCSetMenuA) kfcSetMenuA.clone();
        cloneKFCSetMenuA.setPrice(16.5f);
        cloneKFCSetMenuA.getCola().setName("百事可乐");

        System.out.println("原对象可乐:" + kfcSetMenuA.getCola().getName());
        System.out.println("克隆对象可乐:" + cloneKFCSetMenuA.getCola().getName());

        System.out.println("原对象价格:" + kfcSetMenuA.getPrice());
        System.out.println("克隆对象价格:" + cloneKFCSetMenuA.getPrice());
    }

4、浅拷贝结果

总结:从结果看到,说明克隆后的对象和原始的指向的是同一个cola对象,改名字后都变了,但是基本类型数据是没有改变的。

  • 深拷贝实现方式1

1、重新定义Cola中的clone方法。

java 复制代码
public class Cola implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

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

    @NonNull
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Cola cola = null;

        try {
            cola = (Cola) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }
        return cola;
    }
}

2、实现的时候同时把Cola对象clone一遍。

java 复制代码
    public static void main(String[] args) throws CloneNotSupportedException {
        KFCSetMenuA kfcSetMenuA = new KFCSetMenuA();
        kfcSetMenuA.setPrice(15.5f);
        Cola cola = new Cola();
        cola.setName("可口可乐");
        kfcSetMenuA.setCola(cola);

        KFCSetMenuA cloneKFCSetMenuA = (KFCSetMenuA) kfcSetMenuA.clone();
        //克隆Cola对象
        Cola cloneCola = (Cola) cola.clone();
        cloneKFCSetMenuA.setCola(cloneCola);
        cloneKFCSetMenuA.setPrice(16.5f);
        cloneKFCSetMenuA.getCola().setName("百事可乐");

        System.out.println("原对象可乐:" + kfcSetMenuA.getCola().getName());
        System.out.println("克隆对象可乐:" + cloneKFCSetMenuA.getCola().getName());

        System.out.println("原对象价格:" + kfcSetMenuA.getPrice());
        System.out.println("克隆对象价格:" + cloneKFCSetMenuA.getPrice());
    }

3、深拷贝实现方式1结果:

总结:这个方式就是把克隆对象中引用的对象也进行浅克隆,但是如果出现嵌套多层的时候,每个引用对象都得实现克隆,太麻烦。

  • 深拷贝实现方式2-序列化

1、在KFCSetMenuA 类中加入如下方法,并且KFCSetMenuA 要实现Serializable接口。

java 复制代码
public KFCSetMenuA deepClone() {
        //声明流对象
        ByteArrayOutputStream bos = null;
        ByteArrayInputStream bis = null;
        ObjectOutputStream oos = 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 (KFCSetMenuA) ois.readObject();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                //关闭资源
                bos.close();
                bis.close();
                oos.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2、实现方式

java 复制代码
public static void main(String[] args) throws CloneNotSupportedException {
        KFCSetMenuA kfcSetMenuA = new KFCSetMenuA();
        kfcSetMenuA.setPrice(15.5f);
        Cola cola = new Cola();
        cola.setName("可口可乐");
        kfcSetMenuA.setCola(cola);
        // 序列化深拷贝
        KFCSetMenuA cloneKFCSetMenuA = kfcSetMenuA.deepClone();
        cloneKFCSetMenuA.setPrice(16.5f);
        cloneKFCSetMenuA.getCola().setName("百事可乐");

        System.out.println("原对象可乐:" + kfcSetMenuA.getCola().getName());
        System.out.println("克隆对象可乐:" + cloneKFCSetMenuA.getCola().getName());

        System.out.println("原对象价格:" + kfcSetMenuA.getPrice());
        System.out.println("克隆对象价格:" + cloneKFCSetMenuA.getPrice());
    }

3、深拷贝实现方式2结果:

参考文章:
极客时间《设计模式》(王争)

相关推荐
JerryXZR20 小时前
JavaScript核心编程 - 原型链 作用域 与 执行上下文
开发语言·javascript·原型模式
MinBadGuy2 天前
【GeekBand】C++设计模式笔记10_Prototype_原型模式
c++·设计模式·原型模式
吾与谁归in2 天前
【C#设计模式(5)——原型模式(Prototype Pattern)】
设计模式·c#·原型模式
WaaTong7 天前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
morning_judger8 天前
【设计模式系列】原型模式(十一)
java·设计模式·原型模式
飞升不如收破烂~10 天前
在Spring框架中,容器管理的bean可以有不同的作用域(scope),其中最常用的两种是单例(singleton)和原型(prototype)。
spring·单例模式·原型模式
安泽131410 天前
【修订中】js 中apply call bind 用法
原型模式
Komorebi_999911 天前
JavaScript 判断数据类型有哪些方法?
开发语言·javascript·原型模式
无敌岩雀11 天前
C++设计模式创建型模式———原型模式
c++·设计模式·原型模式
一条晒干的咸魚11 天前
【Web前端】JavaScript 对象原型与继承机制
开发语言·前端·javascript·原型模式·web前端