5分钟聊一聊Java中的原型模式

2024-08-14 09:24:25 浏览数 (1)

在软件开发中,处理对象的创建和管理往往是一个重要的任务。特别是在面对复杂对象时,传统的创建方法可能会导致高昂的时间和资源消耗。原型模式(Prototype Pattern)作为一种创建型设计模式,通过克隆现有对象来创建新对象,从而显著提高了对象创建的效率。本文将深入探讨原型模式的深复制和浅复制,并介绍如何结合 MapStruct 工具类进行对象转换。

原型模式概述

原型模式的核心思想是通过复制现有对象来创建新对象,而不是通过构造函数逐步初始化对象。这样可以避免重复的初始化过程,从而节省时间和资源。尤其在对象创建过程复杂或昂贵的情况下,原型模式能够显著提高效率。它通常用于那些对象创建复杂且开销大的场景,如图形对象、配置对象等。

浅复制与深复制

在实现原型模式时,克隆操作的实现方式主要有两种:浅复制(Shallow Copy)和深复制(Deep Copy)。这两种复制方法在处理对象的内部数据时有所不同。

浅复制

浅复制是指在克隆对象时,仅复制对象的基本数据类型字段和对引用类型字段的引用,而不是这些引用对象本身。这意味着,原对象和克隆对象共享同一个引用类型字段。如果原对象中的引用字段发生改变,克隆对象的相应字段也会受到影响。

以下是一个浅复制的示例代码:

代码语言:java复制
import java.util.Date;

public class Sheep implements Cloneable {
    private String name;
    private Date birth;

    public Sheep(String name, Date birth) {
        this.name = name;
        this.birth = birth;
    }

    @Override
    protected Object clone() {
        Sheep obj = null;
        try {
            obj = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }

    public String getName() {
        return name;
    }

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

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }
}

在这个示例中,Sheep 类实现了 Cloneable 接口,并重写了 clone 方法来实现浅复制。复制后的 Sheep 对象与原对象共享 birth 字段的引用。如果我们修改原对象的 birth 日期,新对象的 birth 日期也会改变。

深复制

深复制是指在克隆对象时,不仅复制对象的基本数据类型字段,还递归地复制对象引用的所有对象。这意味着克隆对象与原对象完全独立,彼此之间没有共享的引用。因此,对一个对象的修改不会影响其他对象。

以下是一个进行深复制的示例代码:

代码语言:java复制
import java.util.Date;

public class Sheep implements Cloneable {
    private String name;
    private Date birth;

    public Sheep(String name, Date birth) {
        this.name = name;
        this.birth = birth;
    }

    @Override
    protected Object clone() {
        Sheep obj = null;
        try {
            obj = (Sheep) super.clone();
            obj.birth = (Date) this.birth.clone(); // 深复制
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }

    public String getName() {
        return name;
    }

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

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }
}

在这个深复制的实现中,我们在 clone 方法中添加了 birth 字段的深复制逻辑。这样,每个 Sheep 对象都拥有自己的 Date 实例。即使我们修改了原对象的 birth 日期,新对象的 birth 日期仍然保持不变。

使用 MapStruct 进行对象转换

MapStruct 是一个用于对象映射的开源框架,它通过编译时生成代码来简化 Java 对象之间的数据转换。MapStruct 提供了一种高效的方式来将一个对象的属性映射到另一个对象,特别适合用于数据传输对象(DTO)和业务对象(BO)之间的转换。

配置 MapStruct

首先,我们需要在项目中添加 MapStruct 的依赖。对于 Maven 项目,pom.xml 中需要加入以下内容:

代码语言:xml复制
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>

对于 Gradle 项目,可以在 build.gradle 文件中添加:

代码语言:groovy复制
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'

定义映射接口

MapStruct 使用接口定义映射规则。我们可以创建一个映射接口来将 Sheep 对象转换为 SheepDTO 对象。

代码语言:java复制
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface SheepMapper {
    SheepMapper INSTANCE = Mappers.getMapper(SheepMapper.class);

    SheepDTO sheepToSheepDTO(Sheep sheep);
    Sheep sheepDTOToSheep(SheepDTO sheepDTO);
}

定义数据传输对象(DTO)

创建 SheepDTO 类作为转换的目标对象:

代码语言:java复制
public class SheepDTO {
    private String name;
    private Date birth;

    // getters and setters
}

使用 MapStruct 进行对象转换

在实际应用中,可以使用 MapStruct 提供的映射功能来进行对象转换。例如:

代码语言:java复制
import java.util.Date;

public class Client {
    public static void main(String[] args) {
        Date date = new Date(1723453691L);
        Sheep sheep = new Sheep("s1", date);

        // 使用 MapStruct 进行转换
        SheepDTO sheepDTO = SheepMapper.INSTANCE.sheepToSheepDTO(sheep);
        System.out.println("SheepDTO Birth: "   sheepDTO.getBirth());

        // 修改原始对象
        date.setTime(15L);
        System.out.println("Original Sheep Birth: "   sheep.getBirth());
        System.out.println("Converted SheepDTO Birth: "   sheepDTO.getBirth());
    }
}

在这个示例中,我们通过 SheepMapperSheep 对象转换为 SheepDTO 对象。这样,我们能够高效地将对象数据传递到不同的模型中,而不必手动编写繁琐的转换代码。

注意,本演示过程中所有对象的属性完全一致的,如果属性不一致的情况下,使用MapStruct需要特别指定

BeanUtils.copyProperties

在 Java 中,BeanUtils.copyProperties 方法是 Apache Commons BeanUtils 库提供的一个用于对象属性复制的工具。这种方法虽然在某些场景下非常便利,但在处理复杂对象映射时存在一些劣势。相比之下,MapStruct 提供了更为强大和灵活的映射功能,特别是在性能和自定义映射规则方面。

BeanUtils.copyProperties 是基于反射实现的。反射操作通常比直接访问对象属性慢得多,因为反射需要在运行时解析类型信息和方法。这种性能开销在大规模的数据转换或者高频调用时可能会成为瓶颈。

代码语言:java复制
import org.apache.commons.beanutils.BeanUtils;
import java.lang.reflect.InvocationTargetException;

public class Client {
    public static void main(String[] args) {
        Source source = new Source("xiaoyu", 30);
        Target target = new Target();
        try {
            BeanUtils.copyProperties(target, source);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println(target.getName()); // xiaoyu
        System.out.println(target.getAge());  // 30
    }
}

总结

原型模式通过克隆对象来提高创建复杂对象的效率,避免了重复的初始化步骤。浅复制和深复制是实现克隆的两种主要方式,浅复制简单但可能导致共享引用的问题,而深复制则更加全面但开销更大。结合 MapStruct 工具类,可以进一步提高对象转换的效率,简化数据传递和处理过程。通过合理地选择克隆方式和使用 MapStruct 进行对象转换,开发者可以在处理复杂对象时实现更高效、更具可维护性的解决方案。


我是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。同时也是一位掘金优秀作者、腾讯云创作之星、阿里云专家博主、华为云云享专家。

0 人点赞