NDM

Enum을 이용해 객체지향적으로 코드 관리하기 본문

기타

Enum을 이용해 객체지향적으로 코드 관리하기

ndm.jr 2023. 6. 28. 17:25

https://ndm-tech.tistory.com/90

에서 상태 패턴(State Pattern)으로 SortType을 제어문 없이, 변경 지점을 줄이며 구현하였습니다.

 

하지만 저는 다음과 같은 문제가 있다고 생각했습니다.

  • 갈수록 늘어나는 구현체 Class
  • 결국 그 하위 구현체는 Dto를 알고있을 수 밖에 없는데, 그렇게 되면 결국 수정 시 관리포인트만 늘어나는 것 아닌가?

때문에

  • 저는 Enum에서 DTO를 아는 것은 결국 막을 수 없는 것이고,
  • 막을 수 없다면 변경 지점을 하나로 모아야 한다고 생각하였으며
  • 정렬방법이 늘어날 때 유사한 코드가 늘어나는 것을 대비하여 switch나 if문을 최대한 제거하였고,
  • 단순 문자열인 정렬방법 과 코드상의 반환값인 Comparator가 연관이 있다는 것을 알려주고자 

 

Enum을 더욱 활용해보기로 했습니다.


## 처음은 이랬습니다

 

@Getter
@RequiredArgsConstructor
public enum TestSortType {
    DISTANCE("distance"), // 거리 순
    POPULAR("popular"); // 인기 순

    private final String code;
}

 

이것을 Controller에서 받아 Service단에서 처리하는 코드가 레거시였습니다. 대략 다음과 같은 형태입니다.

 

@Transactional(readOnly = true)
    public List<ExampleDto> getExampleList( // 각종 파라미터, TestSortType sortBy) {

            Comparator<ExampleDto> comparing;
            if(sortBy.equals(TestSortType.DISTANCE)) comparing = Comparator.comparing(ExampleDto::getDistance);
            else if(sortBy.equals(TestSortType.POPULAR)) comparing = Comparator.comparing(ExampleDto::getPopularNum).reversed();
            else throw new IllegalStateException());
            
            // 비즈니스 로직
            // List<ExampleDto> exampleDtos = getExampleDtos();
            
            return exampleDtos.stream().filter()
                            .sort(comparing)
                            .collect(Collectors.toList());
                            .build();
}

 

여기서 Comparator를 얻는 로직을 메소드로 단순히 추출만 하게 되면 해당 로직이 Service에 남아있게 됩니다.

 

크게 문제는 되지 않았으나

  • Comparator와 SortType간의 관계를 조금 더 코드상에서 표현하고 싶었고
  • if-elseif로 이어지는 유사한 코드도 제거하고 싶었습니다.

## 때문에 이렇게 바꿨습니다.

 

@Getter
@RequiredArgsConstructor
public enum TestSortType {
    DISTANCE("distance", Comparator.comparing(exampleDto::getDistance)), // 거리 순
    POPULAR("popular", Comparator.comparing(exampleDto::getPopularNum).reversed()); // 인기 순

    private final String code;
    private final Comparator<exampleDto> expression;

    public static Comparator<exampleDto> getComparator(TestSortType sortType) {

        return Arrays.stream(TestSortType.values())
                .filter(s -> s.hasSortType(sortType))
                .map(TestSortType::getExpression)
                .findAny()
                .orElseThrow(() -> new IllegalStateException());
    }

    public boolean hasSortType(TestSortType sortType) {
        return Arrays.stream(TestSortType.values())
                .anyMatch(s -> s == sortType);
    }
}

 

이렇게 하면

  • 정렬 관련 수정사항이 있을 때 변경 지점은 TestSortType 클래스로 몰리게 됩니다.
  • code(정렬이름), Comparator 과의 연관성도 잘 나타낼 수 있으며,
  • Service단에서 Comparator를 얻기 위한 로직을 제거할 수도 있습니다.
  • 애초에 Stream을 이용해 추출하기에 if나 switch같은 제어문을 통한 반복 코드도 지울수 있습니다.

 

또한 여기서는 exampleDto를 이용했지만, Generic을 이용한다면 조금 더 범용성 있게 사용할 수도 있다고 생각합니다.

물론 이 경우, [최신순, 인기순] 등 어디서나 쓰이는 정렬 순서를 제외한 [거리순] 같은 특정 도메인에만 적용할 수 있는 정렬 타입이 있다면 어쩔 수 없이 따로 클래스를 만들어주긴 해야할 것 같습니다.

 

리팩터링을 통해 팀원들과 생각을 공유하고, 더 나은 코드가 어떤 것인지 찾는 과정이 정말 재밌었습니다!

 


 

## 참조

 

https://techblog.woowahan.com/2527/

 

Java Enum 활용기 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요? 우아한 형제들에서 결제/정산 시스템을 개발하고 있는 이동욱입니다. 이번 사내 블로그 포스팅 주제로 저는 Java Enum 활용 경험을 선택하였습니다. 이전에 개인 블로그에 E

techblog.woowahan.com