Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- B+TREE
- backend
- 동시성
- Jenkins
- CaffeineCache
- Metaspace
- Spring Data Redis
- 상태패턴
- JAVA8
- java
- ci/cd
- 지연로딩
- nonclustered index
- 카카오 화재
- 재고 시스템
- 트랜잭션
- method area
- GithubActions
- 웹캐시
- Redis
- 공변
- 부하테스트
- 주문
- JDK14
- Ehcache
- JPA
- springboot
- 제네릭
- 리팩터링
- lazyloading
Archives
- Today
- Total
NDM
Enum을 이용해 객체지향적으로 코드 관리하기 본문
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/
'기타' 카테고리의 다른 글
상태 패턴(State Pattern)을 사용하는 방법 (0) | 2023.06.27 |
---|---|
카카오 화재의 발생 원인과 문제점 (0) | 2022.10.19 |
게시판을 만들어보자 1편 : 요구사항 분석과 ERD설계 (0) | 2022.06.21 |