重构 Java 方法,使代码不会被标记为重复
Refactoring a Java method so that the code won't be marked as duplicate
我有一个项目正在使用 Spring 引导和 Spring 数据 JPA 接口。有一个方法出现在 5 个服务中,代码被我的 IDE 标记为重复。我将 post 2 个方法,这样您就可以看到代码的样子,它们基本相同,唯一不同的是使用的存储库接口。
在AccountRequestService.java中我有这个方法:
public Page<AccountRequest> getAccountRequestPageable(final Pageable pageable, final String status) {
Page<AccountRequest> accountRequestPage;
if (UNCOMPLETED_STATUS.equals(status)) {
accountRequestPage = accountRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(pageable,
LoggedUserUtil.getLoggedUserName(), 0, BaseRequestStatusType.APPROVED);
} else {
if (!ALL_STATUS.equals(status)) {
accountRequestPage = accountRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(pageable,
BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName());
} else {
accountRequestPage = accountRequestRepository.findByBaseRequestInitiatorIgnoreCase(pageable, LoggedUserUtil.getLoggedUserName());
}
}
return accountRequestPage;
}
在ApplicationRequestService.java中:
public Page<ApplicationsRequest> getApplicationsRequestPageable(final Pageable pageable, final String status) {
Page<ApplicationsRequest> applicationsRequestPage;
if (UNCOMPLETED_STATUS.equals(status)) {
applicationsRequestPage = applicationRequestRepository
.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(pageable, LoggedUserUtil.getLoggedUserName(), 0,
BaseRequestStatusType.APPROVED);
} else {
if (!ALL_STATUS.equals(status)) {
applicationsRequestPage = applicationRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(
pageable, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName());
} else {
applicationsRequestPage = applicationRequestRepository.findByBaseRequestInitiatorIgnoreCase(pageable, LoggedUserUtil.getLoggedUserName());
}
}
return applicationsRequestPage;
}
我将 post AccountRequestRepository 的代码,因为 ApplicationRequestRepository 是相似的,但指的是对象类型 ApplicationRequest 而不是 AccountRequest:
public interface AccountRequestRepository extends JpaRepository<AccountRequest, Long> {
AccountRequest findByBaseRequestId(Long id);
Page<AccountRequest> findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(Pageable pageable, String initiator,
Integer days, BaseRequestStatusType status);
Page<AccountRequest> findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(Pageable pageable, BaseRequestStatusType baseRequestStatusType, String initiator);
Page<AccountRequest> findByBaseRequestInitiatorIgnoreCase(Pageable pageable, String loggedUserName);
}
现在我想知道是否有任何方法可以重构这段代码,以便我可以摆脱重复的代码。我试图找到一个使用 Java 8 并传递函数的解决方案,但我真的不知道如何对其进行参数化。
我想你需要的是创建“Request”接口并使用Hibernate的Entity Inheritance(Spring数据会理解)来获取Page。参见 https://www.baeldung.com/hibernate-inheritance。
然后,您可以使用 Request 接口放置通用逻辑,甚至可以将 Request 定义为超级 class,您将在其中存储通用逻辑,您的调用!
如果您不能或出于某种原因不想修改现有的 class 结构,仍然有一些方法可以重构此类代码。例如,您可以将 query-functions 作为参数传递给通用函数,该函数决定调用哪个查询,例如
public Page<AccountRequest> getAccountRequestPageable(final Pageable pageable, final String status) {
return getPageable(pageable, status,
p -> accountRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(p, LoggedUserUtil.getLoggedUserName(), 0, BaseRequestStatusType.APPROVED),
p -> accountRequestRepository.findByBaseRequestInitiatorIgnoreCase(p, LoggedUserUtil.getLoggedUserName()),
p -> accountRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(p, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName()));
}
public Page<ApplicationsRequest> getApplicationsRequestPageable(final Pageable pageable, final String status) {
return getPageable(pageable, status,
p -> applicationRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(pageable, LoggedUserUtil.getLoggedUserName(), 0,BaseRequestStatusType.APPROVED),
p -> applicationRequestRepository.findByBaseRequestInitiatorIgnoreCase(pageable, LoggedUserUtil.getLoggedUserName()),
p -> applicationRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(pageable, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName()));
}
public static <T,Q> Page<Q> getPageable(final Pageable pageable, final String status,
Function<Pageable, Page<Q>> findUncompleted,
Function<Pageable, Page<Q>> findAll,
Function<Pageable, Page<Q>> findOther
) {
Page<Q> accountRequestPage;
if (UNCOMPLETED_STATUS.equals(status)) {
accountRequestPage = findUncompleted.apply(pageable);
} else {
if (!ALL_STATUS.equals(status)) {
accountRequestPage = findOther.apply(pageable);
} else {
accountRequestPage = findAll.apply(pageable);
}
}
return accountRequestPage;
}
这样的重构是否有意义取决于项目,不能一概而论。在考虑代码重复时,重要的是要考虑相似性可能只是巧合的可能性。如果两段代码现在看起来很相似,但将来需要独立更改,最好将它们分开。如果他们真的在您的业务领域中共享一个可以抽象出来的基本原则,那么就这样做。换句话说,遵循单一职责原则,即“改变的单一原因”。
我有一个项目正在使用 Spring 引导和 Spring 数据 JPA 接口。有一个方法出现在 5 个服务中,代码被我的 IDE 标记为重复。我将 post 2 个方法,这样您就可以看到代码的样子,它们基本相同,唯一不同的是使用的存储库接口。
在AccountRequestService.java中我有这个方法:
public Page<AccountRequest> getAccountRequestPageable(final Pageable pageable, final String status) {
Page<AccountRequest> accountRequestPage;
if (UNCOMPLETED_STATUS.equals(status)) {
accountRequestPage = accountRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(pageable,
LoggedUserUtil.getLoggedUserName(), 0, BaseRequestStatusType.APPROVED);
} else {
if (!ALL_STATUS.equals(status)) {
accountRequestPage = accountRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(pageable,
BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName());
} else {
accountRequestPage = accountRequestRepository.findByBaseRequestInitiatorIgnoreCase(pageable, LoggedUserUtil.getLoggedUserName());
}
}
return accountRequestPage;
}
在ApplicationRequestService.java中:
public Page<ApplicationsRequest> getApplicationsRequestPageable(final Pageable pageable, final String status) {
Page<ApplicationsRequest> applicationsRequestPage;
if (UNCOMPLETED_STATUS.equals(status)) {
applicationsRequestPage = applicationRequestRepository
.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(pageable, LoggedUserUtil.getLoggedUserName(), 0,
BaseRequestStatusType.APPROVED);
} else {
if (!ALL_STATUS.equals(status)) {
applicationsRequestPage = applicationRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(
pageable, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName());
} else {
applicationsRequestPage = applicationRequestRepository.findByBaseRequestInitiatorIgnoreCase(pageable, LoggedUserUtil.getLoggedUserName());
}
}
return applicationsRequestPage;
}
我将 post AccountRequestRepository 的代码,因为 ApplicationRequestRepository 是相似的,但指的是对象类型 ApplicationRequest 而不是 AccountRequest:
public interface AccountRequestRepository extends JpaRepository<AccountRequest, Long> {
AccountRequest findByBaseRequestId(Long id);
Page<AccountRequest> findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(Pageable pageable, String initiator,
Integer days, BaseRequestStatusType status);
Page<AccountRequest> findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(Pageable pageable, BaseRequestStatusType baseRequestStatusType, String initiator);
Page<AccountRequest> findByBaseRequestInitiatorIgnoreCase(Pageable pageable, String loggedUserName);
}
现在我想知道是否有任何方法可以重构这段代码,以便我可以摆脱重复的代码。我试图找到一个使用 Java 8 并传递函数的解决方案,但我真的不知道如何对其进行参数化。
我想你需要的是创建“Request”接口并使用Hibernate的Entity Inheritance(Spring数据会理解)来获取Page。参见 https://www.baeldung.com/hibernate-inheritance。
然后,您可以使用 Request 接口放置通用逻辑,甚至可以将 Request 定义为超级 class,您将在其中存储通用逻辑,您的调用!
如果您不能或出于某种原因不想修改现有的 class 结构,仍然有一些方法可以重构此类代码。例如,您可以将 query-functions 作为参数传递给通用函数,该函数决定调用哪个查询,例如
public Page<AccountRequest> getAccountRequestPageable(final Pageable pageable, final String status) {
return getPageable(pageable, status,
p -> accountRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(p, LoggedUserUtil.getLoggedUserName(), 0, BaseRequestStatusType.APPROVED),
p -> accountRequestRepository.findByBaseRequestInitiatorIgnoreCase(p, LoggedUserUtil.getLoggedUserName()),
p -> accountRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(p, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName()));
}
public Page<ApplicationsRequest> getApplicationsRequestPageable(final Pageable pageable, final String status) {
return getPageable(pageable, status,
p -> applicationRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(pageable, LoggedUserUtil.getLoggedUserName(), 0,BaseRequestStatusType.APPROVED),
p -> applicationRequestRepository.findByBaseRequestInitiatorIgnoreCase(pageable, LoggedUserUtil.getLoggedUserName()),
p -> applicationRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(pageable, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName()));
}
public static <T,Q> Page<Q> getPageable(final Pageable pageable, final String status,
Function<Pageable, Page<Q>> findUncompleted,
Function<Pageable, Page<Q>> findAll,
Function<Pageable, Page<Q>> findOther
) {
Page<Q> accountRequestPage;
if (UNCOMPLETED_STATUS.equals(status)) {
accountRequestPage = findUncompleted.apply(pageable);
} else {
if (!ALL_STATUS.equals(status)) {
accountRequestPage = findOther.apply(pageable);
} else {
accountRequestPage = findAll.apply(pageable);
}
}
return accountRequestPage;
}
这样的重构是否有意义取决于项目,不能一概而论。在考虑代码重复时,重要的是要考虑相似性可能只是巧合的可能性。如果两段代码现在看起来很相似,但将来需要独立更改,最好将它们分开。如果他们真的在您的业务领域中共享一个可以抽象出来的基本原则,那么就这样做。换句话说,遵循单一职责原则,即“改变的单一原因”。