리팩토링이란

  • 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 방법이다.
  • 코드가 작성된 후에 디자인을 개선하는 작업이다.
  • 모든 것을 미리 생각하기보다는 개발을 하면서 지속적으로 좋은 디자인을 찾는다.
  • 메소드 내의 지역변수와 parameter를 주의 깊게 볼 필요가 있다.
  • 값이 수정되지 않는 변수는 파라미터로 넘길 수 있다.
  • 값이 수정되는 변수는 주의가 필요하다.
  • 변화되는 부분을 함수로 추출하여 리턴 값으로 돌려줄 수 있다.

테스트 -> 리팩토링 -> 테스트 -> 리팩토링 -> 테스트를 하면서 프로그램을 점진적으로 개선하도록 노력한다.

출처 : https://nesoy.github.io/articles/2018-05/Refactoring

예제 리팩토링하기

  • memberId 중간에 변경할 수 없게 final 추가

  • Developer entity가 null이 되면 안되므로 @NonNull어노테이션 추가

  • @Transactional의 readOnly 옵션은 해당 트랜잭션을 읽기 전용으로 설정한다.

    ->해당 옵션은 성능 최적화를 위해서 사용할 수도 있고, 특정 트랜잭션 작업 안에서 쓰기 작업이 일어나는 것을 의도적으로 방지하기 위해서 사용할 수도 있다.

  • getDeveloperByMemberId 부분이 중복해서 사용되므로 Refactor-> Extract Method를 이용해 메소드를 따로 작성해준다.

 private void validateDeveloperLevel(DeveloperLevel developerLevel, Integer experienceYears) {
        if(developerLevel == DeveloperLevel.SENIOR && experienceYears < 10){
            throw new DMakerException(LEVEL_EXPERIENCE_YEAR_NOT_MATCHED);
        }

        if(developerLevel == DeveloperLevel.JUNIOR && experienceYears > 4  ){
            throw new DMakerException(LEVEL_EXPERIENCE_YEAR_NOT_MATCHED);
        }
    }
  • 10, 4와 같은 숫자는 constant로 따로 빼주는 것이 좋다.
package com.fastcampus.programming.dmaker.constant;

public class DMakerConstant {
    public static final Integer MIN_SENIOR_EXPERIENCE_YEARS = 10;
    public static final Integer MAX_JUNIOR_EXPERIENCE_YEARS = 4;

}
  • DMakerContstant로 숫자의 의미 부여
 
 public enum DeveloperLevel {

    NEW("신입 개발자",0,0),
    JUNIOR("주니어 개발자",1,MAX_JUNIOR_EXPERIENCE_YEARS),
    SENIOR("시니어 개발자",MIN_SENIOR_EXPERIENCE_YEARS, 70);

    private final String description;
    private final Integer minExperience;
    private final Integer maxExperience;

}
  • DeveloperLevel에 minExperience와 maxExpereince를 추가한다.

 private void validateDeveloperLevel(DeveloperLevel developerLevel, Integer experienceYears) {

        if(developerLevel.getMinExperience() > experienceYears
                || developerLevel.getMaxExperience() < experienceYears)
            throw new DMakerException(LEVEL_EXPERIENCE_YEAR_NOT_MATCHED);

    }
  • 기존의 코드보다 간결해졌다.

@AllArgsConstructor
@Getter
public enum DeveloperLevel {

    NEW("신입 개발자",years-> years == 0),
    JUNIOR("주니어 개발자",years -> years <= MAX_JUNIOR_EXPERIENCE_YEARS),
    SENIOR("시니어 개발자",years -> years >= MIN_SENIOR_EXPERIENCE_YEARS);

    private final String description;
    private final Function<Integer,Boolean> validateFunction;

    public void validateExperienceYears(Integer years){
        if(!validateFunction.apply(years))
            throw new DMakerException(LEVEL_EXPERIENCE_YEAR_NOT_MATCHED);
    }

}
  • 조금 더 나아가서 Exception을 throw하는 함수를 DeveloperLevel enum class 안에 작성해준다.

  private void validateDeveloperLevel(DeveloperLevel developerLevel, Integer experienceYears) {

        developerLevel.validateExperienceYears(experienceYears);

    }
    
  • validate를 한줄의 코드로 작성이 가능해졌다.

정상적으로 동작하는지 확인하기 위해 테스트 케이스를 추가해준다.

 	@Test
    void createDeveloperTest_fail_with_unmatched_level(){
        //given
        //when
        //then
        DMakerException dMakerException = assertThrows(DMakerException.class,
                () -> dMakerService.createDeveloper(
                        getCreateRequest(SENIOR,FRONT_END,MIN_SENIOR_EXPERIENCE_YEARS-1)));

        assertEquals(LEVEL_EXPERIENCE_YEAR_NOT_MATCHED,
                dMakerException.getDMakerErrorCode());

        dMakerException = assertThrows(DMakerException.class,
                () -> dMakerService.createDeveloper(
                        getCreateRequest(JUNIOR,FRONT_END,MAX_JUNIOR_EXPERIENCE_YEARS+1)));

        assertEquals(LEVEL_EXPERIENCE_YEAR_NOT_MATCHED,
                dMakerException.getDMakerErrorCode());



    }
  • DeveloperLevel과 experieceYears가 맞지 않을 때 Exception을 던져서 기대하는 exception errorCode와 일치하는지 확인한다.

  • 테스트 성공

0개의 댓글