当前位置:

首页 > 编程开发 > Java实体类重构:封装方法提升代码复用性

Java实体类重构:封装方法提升代码复用性

本教程探讨如何在Java项目中有效解决代码重复问题,特别是当多个方法需要对同一实体类(如UserEntity)的集合属性(如角色列表)执行相同的数据转换逻辑时。核心策略是将重复的转换逻辑封装成实体类内部的一个新方法,从而提高代码的可维护性和复用性,避免在不同业务方法中复制代码块。

本文目录

    Java代码重构:通过实体类方法封装重复逻辑提升代码复用性

    本教程旨在解决Java开发中常见的代码重复问题,特别是当多个方法需要对同一实体类(如UserEntity)的集合属性(如角色列表)执行相同的数据转换逻辑时。核心策略是将重复的转换逻辑封装成实体类内部的一个新方法,从而提高代码的可维护性和复用性,避免在不同业务方法中复制代码块。

    问题剖析:业务方法中的代码重复

    在复杂的业务系统中,我们经常会遇到需要在不同服务或映射方法中对同一实体对象进行相同的数据处理或转换。例如,在将UserEntity对象转换为UserDTO或更新UserResource时,可能都需要提取UserEntity中roles集合的ID,并将其转换为String类型的列表。

    考虑以下两个示例方法:

    原始 map 方法: 该方法负责将 UserEntity 映射到 UserDTO。

    import java.util.List;
    import java.util.Date;
    import java.util.stream.Collectors;
    import java.util.Optional; // For userRepository.findById later
    
    // 假设 UserDTO, UserEntity, RoleEntity, UserResource, UserRepository 已定义
    // 以及常量 EMAIL_TYPE
    
    public class UserService { // 示例服务类
    
        private static final String EMAIL_TYPE = "PRIMARY"; // 示例常量
    
        protected UserDTO map(UserEntity entity) {
            var result = new UserDTO();
            // 重复的代码块开始
            var userRoles = entity.getRoles().stream()
                    .map(RoleEntity::getId)
                    .map(String::valueOf)
                    .collect(Collectors.toList());
            // 重复的代码块结束
            result.setId(entity.getId().toString());
            result.setLastAccessDate(entity.getLastAccessDate());
            result.setRoles(userRoles); // 使用转换后的角色ID列表
            if (entity.getEmail() != null) {
                var email = new UserDTO.Email(entity.getEmail(), EMAIL_TYPE);
                result.setEmails(List.of(email));
            }
            return result;
        }
    
        // ... 其他方法 ...
    }

    原始 updateUser 方法: 该方法负责更新用户资源,其中也包含对角色ID的相同处理。

    import java.util.List;
    import java.util.Date;
    import java.util.stream.Collectors;
    import java.util.Optional;
    
    // 假设 UserDTO, UserEntity, RoleEntity, UserResource, UserRepository 已定义
    
    public class UserService { // 示例服务类
    
        private UserRepository userRepository; // 假设已通过构造函数或注解注入
    
        // 假设 mapToUserEntity 方法已定义
        private UserEntity mapToUserEntity(UserResource updatedUser) {
            // 实际的映射逻辑,这里仅作示例
            UserEntity entity = new UserEntity();
            entity.setId(Integer.valueOf(updatedUser.getUserName())); // 假设 userName 是 ID
            entity.setLastAccessDate(updatedUser.getLastAccessDate());
            // 角色设置可能需要从 updatedUser.getRoles() 映射回 RoleEntity
            // 这里为了演示简化,只关注从 optionalUser.get() 获取角色
            return entity;
        }
    
        public UserResource updateUser(String id, UserResource updatedUser) {
            var optionalUser = userRepository.findById(Integer.valueOf(updatedUser.getUserName()));
            if (optionalUser.isEmpty()) {
                // 处理用户不存在的情况,例如抛出异常
                throw new IllegalArgumentException("User not found with ID: " + updatedUser.getUserName());
            }
            // 重复的代码块开始
            updatedUser.setRoles(optionalUser.get().getRoles()
                    .stream()
                    .map(RoleEntity::getId)
                    .map(String::valueOf)
                    .collect(Collectors.toList()));
            // 重复的代码块结束
            updatedUser.setLastAccessDate(optionalUser.get().getLastAccessDate());
            var entity = mapToUserEntity(updatedUser); // 将 UserResource 映射回 UserEntity
            userRepository.save(entity); // 保存更新后的实体
            return updatedUser;
        }
    
        // ... 其他方法 ...
    }

    可以看到,以下代码片段在两个方法中完全重复:

    .getRoles().stream()
    .map(RoleEntity::getId)
    .map(String::valueOf)
    .collect(Collectors.toList());

    这种重复不仅增加了代码量,更重要的是降低了可维护性。一旦角色ID的提取逻辑需要修改(例如,从Integer变为Long,或者需要额外的过滤),就必须同时修改所有出现该逻辑的地方,这极易出错且效率低下。

    解决方案:将领域逻辑封装至实体类

    为了消除这种重复并提高代码的内聚性,最佳实践是将这种与实体自身数据紧密相关的转换逻辑封装到实体类内部。这意味着UserEntity应该“知道”如何提供其角色ID的列表,而不是让外部方法每次都重新计算。

    实现步骤与示例

    1. 在 UserEntity 中添加新方法

      我们将创建一个名为getRoleIds()的新方法,将其添加到UserEntity类中。该方法将负责执行原先重复的流式操作,并返回一个List。

      import java.util.List;
      import java.util.Date;
      import java.util.Collections;
      import java.util.stream.Collectors;
      
      // 假设 RoleEntity 已定义
      class RoleEntity {
          private Integer id;
          private String name;
      
          public Integer getId() { return id; }
          public void setId(Integer id) { this.id = id; }
          public String getName() { return name; }
          public void setName(String name) { this.name = name; }
      }
      
      public class UserEntity {
          private Integer id;
          private String email;
          private Date lastAccessDate;
          private List roles; // 用户拥有的角色列表
      
          // 构造函数、其他属性的getter/setter省略
      
          public Integer getId() { return id; }
          public void setId(Integer id) { this.id = id; }
          public String getEmail() { return email; }
          public void setEmail(String email) { this.email = email; }
          public Date getLastAccessDate() { return lastAccessDate; }
          public void setLastAccessDate(Date lastAccessDate) { this.lastAccessDate = lastAccessDate; }
          public List getRoles() { return roles; }
          public void setRoles(List roles) { this.roles = roles; }
      
          /**
           * 获取用户所有角色的ID列表,并转换为String类型。
           * 如果角色列表为空或为null,则返回一个空列表。
           * @return 包含角色ID(String类型)的列表。
           */
          public List getRoleIds() {
              if (this.roles == null || this.roles.isEmpty()) {
                  return Collections.emptyList(); // 避免NullPointerException,返回空列表
              }
              return this.roles.stream()
                      .map(RoleEntity::getId)
                      .map(String::valueOf)
                      .collect(Collectors.toList());
          }
      }
    2. 重构业务方法

      现在,原始的map和updateUser方法可以调用UserEntity中新添加的getRoleIds()方法,从而极大地简化代码。

      重构后的 map 方法:

      import java.util.List;
      import java.util.Date;
      import java.util.stream.Collectors;
      import java.util.Optional;
      
      public class UserService { // 示例服务类
      
          private static final String EMAIL_TYPE = "PRIMARY"; // 示例常量
      
          // 假设 UserDTO 已定义
          public static class UserDTO {
              private String id;
              private Date lastAccessDate;
              private List roles;
              private List emails;
      
              public String getId() { return id; }
              public void setId(String id) { this.id = id; }
              public Date getLastAccessDate() { return lastAccessDate; }
              public void setLastAccessDate(Date lastAccessDate) { this.lastAccessDate = lastAccessDate; }
              public List getRoles() { return roles; }
              public void setRoles(List roles) { this.roles = roles; }
              public List getEmails() { return emails; }
              public void setEmails(List emails) { this.emails = emails; }
      
              public static class Email {
                  String address;
                  String type;
                  public Email(String address, String type) {
                      this.address = address;
                      this.type = type;
                  }
              }
          }
      
          protected UserDTO map(UserEntity entity) {
              var result = new UserDTO();
              result.setId(entity.getId().toString());
              result.setLastAccessDate(entity.getLastAccessDate());
              result.setRoles(entity.getRoleIds()); // 调用 UserEntity 的新方法
              if (entity.getEmail() != null) {
                  var email = new UserDTO.Email(entity.getEmail(), EMAIL_TYPE);
                  result.setEmails(List.of(email));
              }
              return result;
          }
      
          // ... 其他方法 ...
      }

      重构后的 updateUser 方法:

      import java.util.List;
      import java.util.Date;
      import java.util.stream.Collectors;
      import java.util.Optional;
      import org.springframework.data.jpa.repository.JpaRepository; // 假设使用 Spring Data JPA
      
      // 假设 UserResource 已定义
      public static class UserResource {
          private String userName; // 假设此字段用于查找用户ID
          private List roles;
          private Date lastAccessDate;
      
          public String getUserName() { return userName; }
          public void setUserName(String userName) { this.userName = userName; }
          public List getRoles() { return roles; }
          public void setRoles(List roles) { this.roles = roles; }
          public Date getLastAccessDate() { return lastAccessDate; }
          public void setLastAccessDate(Date lastAccessDate) { this.lastAccessDate = lastAccessDate; }
      }
      
      // 假设 UserRepository 接口已定义
      interface UserRepository extends JpaRepository {}
      
      public class UserService { // 示例服务类
      
          private UserRepository userRepository; // 假设已通过构造函数或注解注入
      
          // 构造函数用于注入 userRepository
          public UserService(UserRepository userRepository) {
              this.userRepository = userRepository;
          }
      
          // 假设 mapToUserEntity 方法已定义
          private UserEntity mapToUserEntity(UserResource updatedUser) {
              UserEntity entity = new UserEntity();
              entity.setId(Integer.valueOf(updatedUser.getUserName())); // 假设 userName 是 ID
              entity.setLastAccessDate(updatedUser.getLastAccessDate());
              // 注意:这里更新实体时,如果 UserResource 也有角色列表,可能需要进一步映射
              // 当前示例主要关注从现有 UserEntity 获取角色ID
              return entity;
          }
      
          public UserResource updateUser(String id, UserResource updatedUser) {
              var optionalUser = userRepository.findById(Integer.valueOf(updatedUser.getUserName()));
              if (optionalUser.isEmpty()) {
                  throw new IllegalArgumentException("User not found with ID: " + updatedUser.getUserName());
              }
              // 调用 UserEntity 的新方法
              updatedUser.setRoles(optionalUser.get().getRoleIds());
              updatedUser.setLastAccessDate(optionalUser.get().getLastAccessDate());
              var entity = mapToUserEntity(updatedUser);
              userRepository.save(entity);
              return updatedUser;
          }
      
          // ... 其他方法 ...
      }

    优势与最佳实践

    1. 代码复用与可维护性: 最直接的优势是消除了重复代码。任何对角色ID提取逻辑的修改都只需在UserEntity.getRoleIds()方法中进行一次,大大降低了维护成本和出错风险。
    2. 提高可读性与意图表达: 业务方法变得更简洁,更清晰地表达了其核心业务逻辑,而不是纠缠于数据转换的细节。例如,entity.getRoleIds()比一长串流式操作更能直观地传达“获取角色ID列表”的意图。
    3. 增强对象内聚性与封装性: 这种做法符合面向对象设计的“高内聚、低耦合”原则。UserEntity现在封装了关于其自身角色数据如何被处理的知识,而不是将这些知识散布在外部服务中。它增强了UserEntity的领域职责。
    4. 命名规范: 选择一个清晰、描述性的方法名至关重要。getRoleIds()明确指出了方法的目的和返回的数据类型。
    5. 空值处理: 在getRoleIds()方法中,我们加入了对roles列表为null或空时的处理(返回Collections.emptyList()),这是一种健壮的编程实践,可以有效避免NullPointerException。

    总结

    通过将重复的、与特定实体数据紧密相关的逻辑封装到实体类内部,我们不仅能够有效消除代码重复,还能显著提升代码的可读性、可维护性和对象设计的内聚性。这种重构策略是构建健壮、可扩展的Java应用的关键一步,鼓励开发者将领域知识和行为尽可能地集中到其所属的领域对象中。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    opencode安装skills教程及本地配置方法
    opencode安装skills教程及本地配置方法

    遇到OpenCode Skills无法加载或配置无效?本文手把手教你完成Skills的安装、环境变量设置及本地调试,避开常见坑点,让AI助手更懂你的代码习惯。

    如何开启Visual Studio2022实时代码分析警告提示
    如何开启Visual Studio2022实时代码分析警告提示

    解决 Visual Studio 2022 编辑时不显示代码分析警告的问题。指南涵盖 .NET 项目实时分析设置、Roslyn 分析器范围调整及错误列表来源筛选,确保开发者能高效利用实时代码诊断功能。

    如何在Visual Studio2022查看变量监视窗口调试程序
    如何在Visual Studio2022查看变量监视窗口调试程序

    详解 Visual Studio 2022 调试技巧:包括断点设置、监视窗口开启方法、变量添加步骤以及通过 F10 单步执行观察变量值变化的全过程。

    Visual Studio2017设置默认编码为UTF-8不带BOM
    Visual Studio2017设置默认编码为UTF-8不带BOM

    针对 Visual Studio 2017 缺乏全局默认编码选项的情况,详解如何利用 .editorconfig 设定项目级 UTF-8 无 BOM 规则,以及手动转换现有文件并校验字节的方法。

    Visual Studio2022F5调试提示启动项目未配置怎么修复
    Visual Studio2022F5调试提示启动项目未配置怎么修复

    解决 Visual Studio 2022 F5 调试报错问题,通过设置启动项目、检查解决方案属性及正确区分类库与应用项目,快速恢复调试功能。

    Visual Studio2022NuGet包还原失败离线处理方案
    Visual Studio2022NuGet包还原失败离线处理方案

    解决Visual Studio 2022离线环境中NuGet包还原失败的难题,涵盖依赖链分析、本地源配置、包集收集及隔离验证步骤,帮助开发者构建稳定的离线开发环境。

    Visual Studio2017大项目加载缓慢,优化加载性能技巧
    Visual Studio2017大项目加载缓慢,优化加载性能技巧

    解决Visual Studio 2017大型项目加载慢的实用指南。涵盖性能监测、扩展管理、文档恢复优化及版本兼容性检查,帮助开发者精准定位瓶颈并提升工作效率。

    Visual Studio2022设置多启动项目同时调试运行
    Visual Studio2022设置多启动项目同时调试运行

    详解 Visual Studio 2022 多启动项目配置方法,包括启动动作选择、顺序调整及调试技巧,帮助开发者高效调试分布式或前后端分离应用。

    fastapi下载文件接口实现教程
    fastapi下载文件接口实现教程

    深入解析在FastAPI框架中实现文件下载接口的完整方案,涵盖基于FileResponse的本地静态文件传输、基于StreamingResponse的分块流式传输、RFC 5987中文文件名编码规范、路径穿越安全防御及BackgroundTasks临时文件清理,并配备详细代码示例与架构解析图。

    码上飞 codeflying 平台功能介绍
    码上飞 codeflying 平台功能介绍

    了解码上飞CodeFlying如何利用自然语言生成应用。通过物品借用登记实例,学习需求解析、代码生成、测试优化及对话修改的核心操作流程。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。