重构一个Java方法,使得代码不会被标记为重复。

huangapple go评论70阅读模式
英文:

Refactoring a Java method so that the code won't be marked as duplicate

问题

我有一个项目正在使用Spring Boot和Spring Data JPA接口。有一个方法在5个服务中出现,我的IDE将该代码标记为重复。我将贴出其中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;
    }

我将贴出 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并传递函数来找到解决方案,但我真的弄不清楚如何参数化这个。

英文:

I have a project that is using Spring boot and Spring Data JPA interfaces. There is a method which appears in 5 services and the code is being marked by my IDE as duplicate. I will post 2 of the methods so you can see how the code looks like, it's basically the same, the only thing that differs is the repository interface used.

In AccountRequestService.java I have this method:


    public Page&lt;AccountRequest&gt; getAccountRequestPageable(final Pageable pageable, final String status) {

        Page&lt;AccountRequest&gt; 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;
    }

In ApplicationRequestService.java:

  public Page&lt;ApplicationsRequest&gt; getApplicationsRequestPageable(final Pageable pageable, final String status) {
        Page&lt;ApplicationsRequest&gt; 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;
    }

I will post the code for AccountRequestRepository, as ApplicationRequestRepository is similar, but refers the object type ApplicationRequest instead of AccountRequest:

public interface AccountRequestRepository extends JpaRepository&lt;AccountRequest, Long&gt; {
    AccountRequest findByBaseRequestId(Long id);

    Page&lt;AccountRequest&gt; findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(Pageable pageable, String initiator,
                                                                                                                       Integer days, BaseRequestStatusType status);

    Page&lt;AccountRequest&gt; findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(Pageable pageable, BaseRequestStatusType baseRequestStatusType, String initiator);

    Page&lt;AccountRequest&gt; findByBaseRequestInitiatorIgnoreCase(Pageable pageable, String loggedUserName);

}

Now I was wondering if there would be any way to refactor this code so that I could get rid of the duplicate. I tried to find a solution using Java 8 and passing around functions but I can't really figure how to parametrize this.

答案1

得分: 1

我认为你需要创建一个名为“Request”的接口,并使用Hibernate的实体继承(这将被Spring Data理解)来获取Page<? extends Request>。请参阅https://www.baeldung.com/hibernate-inheritance。

然后,你可以使用Request接口来放置共有逻辑,甚至可以将Request定义为一个超类,在其中存储那些共有逻辑,由你决定!

英文:

I think what you need is to create "Request" interface and use the Entity Inheritance of Hibernate (which will be understood by Spring Data) to get Page<? extends Request>. See https://www.baeldung.com/hibernate-inheritance.

Then, you can put the common logic using the Request interface, or even define Request as a super class where you'll store that common logic, your call !

答案2

得分: 1

如果您无法或因某些原因不想修改现有的类结构仍然有方法对这种代码进行重构例如您可以将查询函数作为参数传递给一个通用函数该函数决定要调用哪些查询例如

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;
}

是否进行这种重构是否合理取决于项目本身,不能一概而论。在考虑代码重复性时,重要的是要考虑相似性可能只是巧合的可能性。如果现在两个代码段看起来相似,但将来需要独立进行更改,最好将它们分开。如果它们真的在您的业务域中共享一个基本原则,可以将其抽象出来。换句话说,遵循单一职责原则,即“单一更改原因”原则。

英文:

If you cannot or for some reason don't want to modify your existing class structure, there are still ways to refactor such code. You could, for example, pass your query-functions as parameters to a generic function that decides which of the queries to invoke, for example

public Page&lt;AccountRequest&gt; getAccountRequestPageable(final Pageable pageable, final String status) {
return getPageable(pageable, status,
p -&gt; accountRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(p, LoggedUserUtil.getLoggedUserName(), 0, BaseRequestStatusType.APPROVED),
p -&gt; accountRequestRepository.findByBaseRequestInitiatorIgnoreCase(p, LoggedUserUtil.getLoggedUserName()),
p -&gt; accountRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(p, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName()));
}
public Page&lt;ApplicationsRequest&gt; getApplicationsRequestPageable(final Pageable pageable, final String status) {
return getPageable(pageable, status, 
p -&gt; applicationRequestRepository.findByBaseRequestInitiatorIgnoreCaseAndBaseRequestOutdatedDaysGreaterThanAndBaseRequestStatus(pageable, LoggedUserUtil.getLoggedUserName(), 0,BaseRequestStatusType.APPROVED),    			
p -&gt; applicationRequestRepository.findByBaseRequestInitiatorIgnoreCase(pageable, LoggedUserUtil.getLoggedUserName()),
p -&gt; applicationRequestRepository.findByBaseRequestStatusAndBaseRequestInitiatorIgnoreCase(pageable, BaseRequestStatusType.valueOf(status), LoggedUserUtil.getLoggedUserName()));                            
}
public static &lt;T,Q&gt; Page&lt;Q&gt; getPageable(final Pageable pageable, final String status, 
Function&lt;Pageable, Page&lt;Q&gt;&gt; findUncompleted,
Function&lt;Pageable, Page&lt;Q&gt;&gt; findAll,
Function&lt;Pageable, Page&lt;Q&gt;&gt; findOther
) {
Page&lt;Q&gt; 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;
}

Whether or not such a refactoring makes sense depends on the project, and cannot be answered in general. When thinking about code duplication, it is important to consider the possibility that the similarity may just be coincidence. If two segments code look similar now, but will need to change independently in the future, it may be better to keep them separate. If they really share an underlying principle in your business domain that can be abstracted out, do so. In other words, follow the Single Responsibility Principle, in the sense of "single reason to change".

huangapple
  • 本文由 发表于 2020年9月2日 22:19:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/63707575.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定