创建型设计模式-原型模式,Java怎么实现

一、什么是原型模式

1.1 概述

一句话:用一个已经创建的实力作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需依赖于构造函数。使用原型模式,可以在运行时动态地创建对象,避免了使用显式的构造函数进行对象创建的过程。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

在Java中,原型模式的实现通常需要实现Cloneable接口,并重写clone()方法。Cloneable接口是一个标记接口,用于指示该类可以被克隆。

1.2 原型模式的结构

graph LR A(原型模式) B(抽象原型类) C(具体原型类) D(访问类) E(规定了具体原型对象必须实现clone方法) F(实现抽象原型类的clone方法,它是可被复制的对象) G(使用具体原型类中的clone方法来复制新的对象) A ---> B ---> E A ---> C ---> F A ---> D ---> G style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px style F fill:#ADD8E6,stroke:#ADD8E6,stroke-width:2px style G fill:#00FFFF,stroke:#00FFFF,stroke-width:2px

二、Java中怎么实现原型模式

Java中原型模式的克隆分为深克隆和浅克隆。

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

深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

graph LR A(Java实现原型模式) B(浅克隆) C(深克隆) D(序列化和反序列化) E(自定义克隆方法) A ---> B A ---> C C ---> D C ---> E style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px

2.1 在Java中如何实现浅克隆

Java中的克隆操作默认是浅拷贝,即复制对象本身,但不复制对象内部的引用类型数据。

下面是浅克隆实现的案例

  • 创建一个可克隆的抽象基类或接口。该基类或接口将定义克隆方法,用于复制对象。
java 复制代码
public abstract class Prototype implements Cloneable {
    public abstract Prototype clone();
}
  • 创建具体类,该类将实现克隆方法并定义对象的特定行为。(具体类中实现克隆方法。使用super.clone()方法来复制对象,并进行类型转换。)
java 复制代码
public class ConcretePrototype extends Prototype {
    @Override
    public Prototype clone() {
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}
  • 在客户端代码中使用原型对象进行克隆操作。
java 复制代码
public class Test {

    public static void main(String[] args) {
        Prototype original = new ConcretePrototype();
        Prototype clone = original.clone();
    }
    
}

2.2 在Java中如何实现深克隆

在Java中,有两种方法可以实现深克隆。

graph LR A(java实现深克隆) ---> B(序列化和反序列化) A(java实现深克隆) ---> C(自定义克隆方法) style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px

2.2.1 通过实现Serializable接口进行序列化和反序列化

  • 在需要深克隆的类中实现Serializable接口。

  • 将对象写入输出流并进行序列化。

  • 从输入流中读取对象并进行反序列化。

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

public class Demo implements Serializable {
    private String name;
    private MyObject myObject;

    // 构造函数和其他方法

    public Demo deepCopy() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (Demo) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

2.2.2 使用自定义的深克隆方法

  • 在需要深克隆的类中实现自定义的深克隆方法。

  • 在深克隆方法中创建一个新对象,并将原对象的属性值逐一复制到新对象中。对于引用类型的属性,可以使用其自身的深克隆方法或递归进行克隆。

java 复制代码
public class Demo {
    private String name;
    private MyObject myObject;

    // 构造函数和其他方法

    public Demo deepCopy() {
        Demo newObject = new Demo();
        newObject.setName(this.name);
        newObject.setMyObject(this.myObject.deepCopy()); // 假设MyObject类也实现了深拷贝方法
        return newObject;
    }
}

2.3 spring中那些非常好用的原型模式工具类

Spring框架还提供了其他一些原型模式相关的工具类和功能,包括以下几个:

graph LR A(Spring提供的原型模式工具类) B(ObjectFactory) C(PrototypeScope) D(ObjectProvider) E(Lookup注解) A ---> B A ---> C A ---> D A ---> E style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px

ObjectFactory: ObjectFactory是Spring提供的一个用于创建原型对象的接口,它可以用于在运行时动态地创建对象,而无需显式地调用构造函数。ObjectFactory接口有一个重要的方法getObject(),用于获取原型对象的实例。该方法在每次调用时都会返回一个新的对象实例,因此适用于创建原型对象。

PrototypeScope: PrototypeScope是Spring框架中的一个作用域实现,用于定义原型作用域的Bean。可以通过在Bean定义中指定@Scope("prototype")注解或在XML配置中使用元素将Bean定义为原型作用域。使用PrototypeScope作用域,每次获取Bean时都会创建一个新的实例。

ObjectProvider: ObjectProvider是Spring 4.3引入的一个接口,用于获取对象实例。它可以用于获取原型对象的实例,类似于ObjectFactory。ObjectProvider提供了更多的方法来获取和管理对象实例,例如getIfAvailable()、getIfUnique()等。

@Lookup注解:@Lookup注解是Spring框架中的一个元注解,用于标记一个方法,表示该方法应该返回一个原型对象。使用@Lookup注解,可以在运行时动态地获取原型对象的实例。

示例:展示了如何在Spring中使用ObjectFactory创建原型对象:

java 复制代码
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class MyPrototype {
    private static int count = 0;
    private int id;

    public MyPrototype() {
        id = count++;
    }

    public int getId() {
        return id;
    }
}

public class PrototypeExample {
    public static void main(String[] args) {
        ObjectFactory<MyPrototype> objectFactory = new ObjectFactory<MyPrototype>() {
            @Override
            public MyPrototype getObject() {
                return new MyPrototype();
            }
        };

        // 使用ObjectFactory创建原型对象
        MyPrototype obj1 = objectFactory.getObject();
        MyPrototype obj2 = objectFactory.getObject();

        System.out.println("Object 1 ID: " + obj1.getId());
        System.out.println("Object 2 ID: " + obj2.getId());
    }
}

在上面的示例中,使用ObjectFactory创建了一个MyPrototype的原型对象。每次调用getObject()方法时,都会返回一个新的MyPrototype对象实例。通过多次调用,可以看到每个对象实例都有不同的ID。

ObjectFactory接口可以在Spring框架中的各个地方使用,例如在Bean定义中声明原型作用域的Bean时,可以使用ObjectFactory来创建原型对象。

2.4 spring的BeanUtils.copyProperties 有用到原型设计模式吗

BeanUtils.copyProperties()方法并**没有**直接使用原型设计模式。它是Spring框架中的一个工具方法,用于将一个Java对象的属性值复制到另一个Java对象中。

在BeanUtils.copyProperties()方法的实现中,并没有显式地使用原型模式的概念。它主要通过反射机制来获取和设置对象的属性值,然后将源对象的属性值复制到目标对象中。

然而,BeanUtils.copyProperties()方法在某种程度上**可以看作是一种原型模式的变种**。它通过复制属性值的方式创建一个新的目标对象,而不需要显式地调用构造函数。这种方式类似于原型模式中通过复制现有对象来创建新对象的方式,但并不是严格的原型模式实现。

总的来说,尽管BeanUtils.copyProperties()方法在某种程度上具有类似原型模式的特征,但它并不是原型模式的典型实现。它更多地用于对象属性值的复制和转换,而不是动态创建对象实例。

三、原型模式的意义和Java中的使用场景

3.1 意义

在运行期建立和删除原型。

3.2 Java中的使用场景

graph LR A(使用场景) B(多线程环境) C(缓存管理) D(对象池管理) E(动态代理) A ---> B A ---> C A ---> D A ---> E style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px
  • 多线程环境:在多线程环境中,原型模式工具类可以用于创建线程安全的对象实例。每个线程可以通过原型模式工具类获取自己的对象实例,避免了多个线程之间的竞争和共享状态的问题。

  • 缓存管理:原型模式工具类可以用于缓存管理,特别是在需要将某个对象作为模板复制多个对象的情况下。通过原型模式工具类,可以复制模板对象,并对每个复制的对象进行个性化的修改,从而实现自定义的缓存对象。

  • 对象池管理:在需要管理对象池的场景中,原型模式工具类可以用于创建和管理对象池中的对象。通过原型模式工具类,可以复制原型对象来填充对象池,并在需要时获取对象实例,避免了频繁创建和销毁对象的开销。

  • 动态代理:在动态代理中,原型模式工具类可以用于创建代理对象。每次需要代理对象时,可以通过原型模式工具类获取一个新的代理对象实例,以确保每个代理对象都具有独立的状态和行为。

四、原型模式的优缺点和注意事项

  • 优点

    • 性能提高。

    • 逃避构造函数的约束。

  • 缺点

    • 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

    • 必须实现 Cloneable 接口。

  • 注意事项

    • 与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
相关推荐
NiNg_1_2343 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk5 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*5 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue5 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man5 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
Wx-bishekaifayuan6 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer086 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
WaaTong6 小时前
《重学Java设计模式》之 单例模式
java·单例模式·设计模式