原型模式(创建型)

一、前言

原型模式是一种创建型设计模式,它允许在运行时通过克隆现有对象来创建新对象,而不是通过常规的构造函数创建。在原型模式中,一个原型对象可以克隆自身来创建新的对象,这个过程可以通过深度克隆或浅克隆来实现。简单说原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。

原型模式包含三个角色:

①Prototype(抽象原型类):定义用于克隆自身的接口,通常是一个抽象类或接口,其中声明了一个克隆方法 clone(),用于创建一个新的对象,具体的克隆操作由子类实现。

②ConcretePrototype(具体原型类):实现 Prototype 接口,实现 clone() 方法,完成对象的克隆操作。每个具体原型类都有自己的一些属性和方法,不同的具体原型类之间具有不同的实现方式。

③Client(客户类):使用原型模式创建新的对象,在原型模式中,客户端通过克隆接口来创建新的对象,而不是通过实例化的方式。客户端需要获取一个原型对象,并通过克隆方法来创建新的对象,从而避免了创建新对象的开销。

原型模式的克隆分为浅拷贝和深拷贝两种。

二、浅拷贝

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

这里抽象原型类不用自己创建,都是Object,里面有clone()方法:

具体原型类实现Cloneable,里面重写clone方法,直接super调用父类的clone方法即可:

java 复制代码
import java.util.List;

/**
 * @Author dengyifan
 * @create 2023/11/10 10:19
 * @description
 */
public class UserPrototype implements Cloneable{

    private String name;

    private Integer age;

    private List<String> messages;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public List<String> getMessages() {
        return messages;
    }

    public void setMessages(List<String> messages) {
        this.messages = messages;
    }

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

客户类:

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * @Author dengyifan
 * @create 2023/11/10 10:22
 * @description
 */
public class PrototypeClient {
    public static void main(String[] args) throws CloneNotSupportedException {
        UserPrototype userPrototype1 = new UserPrototype();

        userPrototype1.setName("tom");
        userPrototype1.setAge(18);
        List<String> messages1 = new ArrayList<>();
        messages1.add("test");
        userPrototype1.setMessages(messages1);

        UserPrototype userPrototype2 = userPrototype1.clone();

        System.err.println(String.format("两个对象是否一致:%s", userPrototype1 == userPrototype2));
        System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototype1.getName() == userPrototype2.getName()));

        System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototype1.getMessages() == userPrototype2.getMessages()));

        System.err.println(userPrototype1.getMessages());
        System.err.println(userPrototype2.getMessages());

        messages1.clear();
        messages1.add("test1");
        System.err.println(userPrototype1.getMessages());
        System.err.println(userPrototype2.getMessages());


    }
}

运行结果:

通过结果可以看到,通过克隆的对象与原型对象的引用不一致,说明再内存中存在两个不同的对象,一个是原型对象,一个是克隆生成的对象。而这里属性都显示是一致的,结果都是true,说明两个对象的成员对象是同一个,也就是对象本身是复制了,但是其成员的对象再内存中没有复制。并且我们修改原型的messages属性,克隆对象的messages也跟着改变,说明属性确实是同一个对象的引用。

三、深拷贝

深拷贝中除了原型对象与克隆生成的对象被复制以外,里面的属性也需要被复制,即地址都不一样。这里深拷贝实现了Serializable,即进行了序列化。

java 复制代码
import java.io.*;
import java.util.List;

/**
 * @Author dengyifan
 * @create 2023/11/10 15:43
 * @description
 */
public class UserPrototypeDeep implements Serializable {
    private String name;

    private Integer age;

    private List<String> messages;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public List<String> getMessages() {
        return messages;
    }

    public void setMessages(List<String> messages) {
        this.messages = messages;
    }

    protected UserPrototypeDeep deepClone() throws Exception{
        ByteArrayOutputStream bao=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bao);
        oos.writeObject(this);
        //将对象从流中取出
        ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(bis);
        return (UserPrototypeDeep) ois.readObject();
    }
}
java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * @Author dengyifan
 * @create 2023/11/10 15:46
 * @description
 */
public class PrototypeDeepClient {
    public static void main(String[] args) throws Exception {
        UserPrototypeDeep userPrototypeDeep1 = new UserPrototypeDeep();
        userPrototypeDeep1.setAge(19);
        userPrototypeDeep1.setName("tom");
        List<String> messages1 = new ArrayList<>();
        messages1.add("test");
        userPrototypeDeep1.setMessages(messages1);

        UserPrototypeDeep userPrototypeDeep2 = userPrototypeDeep1.deepClone();

        System.err.println(String.format("两个对象是否一致:%s", userPrototypeDeep1 == userPrototypeDeep2));
        System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototypeDeep1.getName() == userPrototypeDeep2.getName()));

        System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototypeDeep1.getMessages() == userPrototypeDeep2.getMessages()));

        System.err.println(userPrototypeDeep1.getMessages());
        System.err.println(userPrototypeDeep2.getMessages());
        messages1.clear();
        messages1.add("test1");
        System.err.println(userPrototypeDeep1.getMessages());
        System.err.println(userPrototypeDeep2.getMessages());

    }
}

运行结果:

可以看出除了对象本身不一致以外,里面的属性也不一致,当修改原型对象里面引用对象的属性时,克隆对象的属性不会发生变化,即也证明属性不是同一个对象。

对于原型模式的应用场景,主要有以下几点:

  1. 当对象的创建过程非常复杂或耗费大量资源时,可以使用原型模式来复制一个现有对象,从而避免重复创建对象。

  2. 当需要创建多个相似的对象时,可以使用原型模式来减少重复代码,提高代码的重用性。

  3. 当创建对象的过程需要大量的数据准备或配置时,可以使用原型模式来复制一个已经配置好的对象,从而避免重复的数据准备或配置过程。

  4. 当需要动态生成对象的子类时,可以使用原型模式来复制一个已有的对象,并对其进行修改,从而避免重新编写子类的代码。

  5. 当需要保护对象的状态时,可以使用原型模式来创建对象的副本,并将副本交给其他对象使用,从而避免原始对象的状态被修改。

相关推荐
MZ_ZXD001几秒前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
PP东3 分钟前
Flowable学习(二)——Flowable概念学习
java·后端·学习·flowable
ManThink Technology8 分钟前
如何使用EBHelper 简化EdgeBus的代码编写?
java·前端·网络
invicinble12 分钟前
springboot的核心实现机制原理
java·spring boot·后端
人道领域20 分钟前
SSM框架从入门到入土(AOP面向切面编程)
java·开发语言
大模型玩家七七40 分钟前
梯度累积真的省显存吗?它换走的是什么成本
java·javascript·数据库·人工智能·深度学习
CodeToGym1 小时前
【Java 办公自动化】Apache POI 入门:手把手教你实现 Excel 导入与导出
java·apache·excel
凡人叶枫1 小时前
C++中智能指针详解(Linux实战版)| 彻底解决内存泄漏,新手也能吃透
java·linux·c语言·开发语言·c++·嵌入式开发
JMchen1232 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
阔皮大师2 小时前
INote轻量文本编辑器
java·javascript·python·c#