Рефакторинг метода Java, чтобы код не был помечен как дубликат

#java #spring #refactoring

#java #spring #рефакторинг

Вопрос:

У меня есть проект, который использует интерфейсы 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 и передавая функции, но я не могу понять, как это параметризовать.

Комментарии:

1. Похоже, вы опубликовали тот же код из того же класса.

2. @Hulk Вот как работают интерфейсы JPA, вам в основном нужно написать код, подобный этому, чтобы метод мог быть преобразован в SQL-запрос. Я думаю, что другой альтернативой было бы написать собственный запрос и присвоить методу другое имя.

Ответ №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;
}
  

Имеет ли смысл такой рефакторинг, зависит от проекта, и на него нельзя ответить в целом. Когда вы думаете о дублировании кода, важно учитывать возможность того, что сходство может быть просто совпадением. Если код двух сегментов сейчас выглядит похожим, но в будущем его необходимо будет изменить независимо, возможно, будет лучше сохранить их отдельно. Если они действительно разделяют основополагающий принцип в вашей бизнес-области, который можно абстрагировать, сделайте это. Другими словами, следуйте принципу единой ответственности в смысле «единственной причины для изменения».

Ответ №2:

Я думаю, что вам нужно, это создать интерфейс «запрос» и использовать сущность наследования гибернации (которые будут понятны Весна данных), чтобы получить страницу<? расширяет запрос>. See https://www.baeldung.com/hibernate-inheritance.

Затем вы можете поместить общую логику, используя интерфейс запроса, или даже определить запрос как суперкласс, где вы будете хранить эту общую логику, ваш вызов !

Комментарии:

1. Спасибо, это тоже хорошее решение! Я протестировал его, и он работает просто отлично, просто я хотел сделать это с помощью Java 8.