
本文探讨了在Java项目中,当不同方法中存在重复的代码逻辑时,如何通过重构来提高代码的可维护性和可读性。通过将重复的逻辑封装到一个新的辅助方法中,并将其合理地放置在相关实体类中,可以有效消除代码冗余,遵循DRY(Don’t Repeat Yourself)原则,从而优化代码结构和提升开发效率。
一、问题背景与代码冗余分析
在软件开发中,代码复用是提高效率和维护性的关键。然而,在实际项目中,我们经常会遇到不同方法中存在相似或完全相同的代码片段。这种代码冗余不仅增加了维护成本,也使得代码难以阅读和理解。
以下面的Java代码为例,我们有两个方法map和updateUser,它们都涉及从UserEntity中提取RoleEntity的ID并转换为字符串列表的逻辑:
原始map方法:
protected UserDTO map(UserEntity entity) { var result = new UserDTO(); // 重复逻辑片段 A 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;}
原始updateUser方法:
public UserResource updateUser(String id, UserResource updatedUser) { var optionalUser = userRepository.findById(Integer.valueOf(updatedUser.getUserName())); // 重复逻辑片段 B 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());
虽然这段代码逻辑不长,但它的重复出现违反了DRY原则。当未来业务需求变化,需要修改角色ID的提取或转换方式时,我们将不得不在多个地方进行修改,这极易引入错误。
二、重构策略:封装重复逻辑到新方法
解决代码冗余最有效的方法是将其抽象为一个独立的、职责单一的方法。尽管有时开发者可能希望避免创建“额外”的方法,但从长远来看,这种封装带来的好处远大于其“额外”的成本。
核心思想:将重复的逻辑提取出来,封装成一个新的方法。这个新方法应该具有清晰的职责,并且可以被多个调用方复用。
最佳实践:将方法放置在相关实体中对于上述场景,重复的逻辑是关于从UserEntity中获取其关联的RoleEntity的ID列表。这种操作本质上是UserEntity自身数据的一种派生表示。因此,将这个新方法添加到UserEntity类中,使其成为UserEntity的一个行为,是符合面向对象设计原则(如“告诉,不要询问”)的最佳实践。
1. 创建辅助方法
在UserEntity类中添加一个名为getRoleIds()的公共方法,用于封装提取角色ID的逻辑:
// UserEntity.javapublic class UserEntity { private Long id; private String email; private Date lastAccessDate; private List roles; // 假设RoleEntity包含getId()方法 // ... 其他属性和方法 /** * 获取用户所有角色的ID列表。 * @return 包含角色ID字符串的列表。 */ public List getRoleIds() { if (this.roles == null) { return Collections.emptyList(); // 处理roles为null的情况 } return this.roles.stream() .map(RoleEntity::getId) .map(String::valueOf) .collect(Collectors.toList()); } // Getter和Setter方法 public List getRoles() { return roles; } public void setRoles(List roles) { this.roles = roles; } public Long getId() { return id; } public void setId(Long 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; }}
2. 重构原始方法
现在,我们可以修改map和updateUser方法,用新创建的getRoleIds()方法替换掉重复的逻辑。
重构后的map方法:
protected UserDTO map(UserEntity entity) { var result = new UserDTO(); // 直接调用UserEntity的getRoleIds()方法 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 existingUser = optionalUser.get(); // 直接调用UserEntity的getRoleIds()方法 updatedUser.setRoles(existingUser.getRoleIds()); updatedUser.setLastAccessDate(existingUser.getLastAccessDate()); } var entity = mapToUserEntity(updatedUser); userRepository.save(entity); return updatedUser;}
三、重构带来的益处与注意事项
通过上述重构,我们获得了以下显著的益处:
消除代码冗余(DRY原则):重复的逻辑被集中管理,避免了多处修改的风险。提高可读性:方法调用entity.getRoleIds()比一长串Stream操作更具表达性,代码意图一目了然。增强可维护性:如果角色ID的提取逻辑需要改变(例如,从Long变为UUID,或者需要过滤某些角色),只需修改UserEntity中的getRoleIds()方法一处即可,所有调用方都会自动更新。更好的封装性:UserEntity现在负责管理其内部角色ID的表示方式,外部类无需关心其内部实现细节。简化测试:getRoleIds()方法可以独立进行单元测试,确保其逻辑的正确性。
注意事项:
命名清晰:新创建的方法名应准确反映其功能,如getRoleIds()清晰地表明了获取角色ID的职责。处理空值:在getRoleIds()方法中,考虑roles列表为null的情况,返回Collections.emptyList()是一个稳健的做法,避免空指针异常。职责划分:确保新方法的职责单一。如果一个方法开始承担多个不相关的任务,它可能需要进一步拆分。
四、总结
在软件开发中,面对代码冗余时,积极进行重构是提升代码质量的关键。将重复的逻辑封装到职责单一的辅助方法中,并将其放置在最合适的类中(通常是数据实体类),能够显著提高代码的可读性、可维护性和健壮性。这种看似简单的“额外”方法,实则是遵循面向对象设计原则、构建高质量软件的基石。
以上就是如何在同一类中优化方法间重复逻辑的调用的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/96896.html
微信扫一扫
支付宝扫一扫