Java代码重构:通过领域模型扩展消除方法内重复逻辑

张开发
2026/5/1 22:22:43 15 分钟阅读

分享文章

Java代码重构:通过领域模型扩展消除方法内重复逻辑
本文讨论了在Java类中消除重复代码的有效策略。本文提出了将通用数据转换逻辑包装到实体本身的新方法以处理不同方法中的同一实体如Userentity的重复逻辑。通过将角色ID提取逻辑转移到Userentity的getroleids不仅简化了调用方代码而且提高了代码的可读性和可维护性并遵循了面向对象的设计原则。识别和消除代码重复在软件开发中代码重复是一个常见的问题可以降低代码的可读性和可维护性增加引入bug的风险。当我们在同一类别的不同方法中找到相同的代码片段时这通常是一个清晰的重构信号。考虑到以下两种Java方法它们分别用于映射用户实体到DTO更新用户资源// 方法一将Userentity映射到Userdtoto上 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); if (entity.getEmail() ! null) { var email new UserDTO.Email(entity.getEmail(), EMAIL_TYPE); result.setEmails(List.of(email)); } return result; } // 方法二:更新用户资源 public UserResource updateUser(String id, UserResource updatedUser) { var optionalUser userRepository.findById(Integer.valueOf(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); userRepository.save(entity); return updatedUser; }以下代码片段在这两种方法中重复.getRoles() .stream() .map(RoleEntity::getId) .map(String::valueOf) .collect(Collectors.toList());本代码的目的是从Userentity(或其相关的Roleentity列表)中提取所有角色的ID并将其转换为字符串列表。这种重复不仅使代码冗长而且如果未来需要修改角色ID的提取逻辑必须在多个地方修改容易出错。重构策略扩展领域模型为了消除这种重复我们应该遵循“Dont Repeat Yourself”DRY原则。符合面向对象设计原则的最直接、最直接的解决方案是将这种与Userentity密切相关的逻辑包装到Userentity本身的一种新方法中。这样做的好处是:包装增强:将数据(RoleEntity列表)和处理数据(提取角色ID)的逻辑放在一起符合对象的单一责任原则。API清晰:Userentity现在提供了更高层次的语义方法来获取角色ID列表而不是暴露其内部的Roleentity集合并进行外部处理。简化调用方其他方法可以直接调用这种新方法而无需关注内部实现细节。实现细节在Userentity中添加辅助方法我们将创建一种叫做getroleids()的新方法并将其添加到userentity类中。该方法将负责提取角色ID并将其转换为字符串列表的所有逻辑。首先修改Userentity类添加getroleids()方法// UserEntity.java public class UserEntity { private Integer id; private String email; private Date lastAccessDate; private ListRoleEntity roles; // 假设Roleentity包含getid()方法 // ... 其他属性和getter/setter方法 ... /** * 获取用户所有角色的ID列表。 * return 字符串列表的角色ID。 */ public ListString getRoleIds() { if (this.roles null) { return Collections.emptyList(); } return this.roles.stream() .map(RoleEntity::getId) .map(String::valueOf) .collect(Collectors.toList()); } } // RoleEntity.java (示例) public class RoleEntity { private Integer id; private String name; // ... getter/setter ... public Integer getId() { return id; } }接下来我们可以在原始map和updateuser方法中调用这种新方法以消除重复代码import java.util.Collections; import java.util.List; import java.util.stream.Collectors; // 方法一:重构map方法 protected UserDTO map(UserEntity entity) { var result new UserDTO(); // 调用Userentity的新方法代码更简洁 var userRoles entity.getRoleIds(); result.setId(entity.getId().toString()); result.setLastAccessDate(entity.getLastAccessDate()); result.setRoles(userRoles); if (entity.getEmail() ! null) { var email new UserDTO.Email(entity.getEmail(), EMAIL_TYPE); result.setEmails(List.of(email)); } return result; } // 方法二:重构后updateuser方法 public UserResource updateUser(String id, UserResource updatedUser) { var optionalUser userRepository.findById(Integer.valueOf(updatedUser.getUserName())); if (optionalUser.isPresent()) { // 调用Userentity的新方法代码更简洁 updatedUser.setRoles(optionalUser.get().getRoleIds()); updatedUser.setLastAccessDate(optionalUser.get().getLastAccessDate()); } var entity mapToUserEntity(updatedUser); userRepository.save(entity); return updatedUser; }通过这种方式我们成功地将重复逻辑包装到Userentity类中使调用方代码更加简洁易读。优点和最佳实践这种重建方法带来了许多好处通过语义化的方法名(如getroleIds()提高代码可读性代码的意图一目了然。增强可维护性:如果需要修改角色ID的提取逻辑只需要在一个地方UserEntity.getRoleIds()修改大大降低了维护成本和引入新bug的风险。遵循面向对象原则将与Userentity数据相关的行为包装在Userentity内部符合包装和单一责任原则。这使得Userentity成为一个更“智能”的领域对象。减少错误重复代码是错误的温床。消除重复可以有效地减少因复制粘贴错误或遗漏修改而产生的bug。

更多文章