[사다리 - 함수형 프로그래밍] 박진형 리팩터링#87
[사다리 - 함수형 프로그래밍] 박진형 리팩터링#87jinhyeongpark wants to merge 47 commits intonext-step:jinhyeongparkfrom
Conversation
- 각 값들은 생성자를 private로 하고 정적 팩터리 메서드를 통해 검증 후 생성 - 가로 검증: 최소 두 명의 참가자가 있도록 value < 2 - 세로 검증: 1 이상의 값을 입력하도록 value < 1
- 불필요한 객체 생성을 방지하기 위해 정적 팩터리 메서드 및 캐싱 적용 - boolean 원시 값 포장 및 캡슐화
- 정적 팩터리 메서드를 통해 Point 리스트 생성 및 객체 조립 로직 구현 - 가로 라인이 겹치지 않도록 연속 생성 방지 로직 적용
- LadderWidth와 LadderHeight를 기반으로 사다리 조립 로직 구현 - List<Line>을 관리하는 일급 컬렉션 구조 설계
- 반복적으로 쓰이는 사다리 시각화 문자열에 대한 매직넘버 처리 - printLadder를 통해 각 Line들을 구현 - 각 Line은 세로줄 및 Point에 따라 가로줄 출력
- 예외 발생 시 재입력을 유도하는 공통 입력 유틸리티 메서드 구현 (Supplier 활용) - 사다리 너비 및 높이 입력 기능 구현 - 정수 입력값에 대한 기본 형식 검증 및 에러 메시지 처리
- InputView, Ladder, OutputView를 연결하여 전체 게임 흐름 제어
- 외부에서 boolean을 주입하는 strategy로 변경 - 차후 용이한 테스트코드 작성을 위함
- 우측에 여유 공간이 있고 포인트가 있으면 우로 이동 - 좌측에 여유 공간이 있고, 포인트가 있으면 좌로 이동 - 그렇지 않으면 그대로 내려오기
- climb: 각 인덱스에서 시작하여 Line을 모두 거쳐 도착지의 인덱스를 반환 - generateResults: 모든 인덱스에 대해 climb를 호출하여 그 결과를 반환
- Point를 3개로 지정하고 전략을 true로 하여 기둥이 true, false, true로 생성되게 함 - 각 기둥에서 이동하여 도착한 인덱스를 검증
- climb: 참여자를 2, 높이를 1, 전략을 true로 설정하여 교차해서 내려감을 검증 - generateResults: 참여자를 3, 높이를 1, 전략을 true로 설정하여 결과의 크기와 그 값을 검증
- 기존 LadderWidth는 사용 X - Participants를 받아 그 크기로 반복
- Name: 빈 값이거나 5글자를 넘을 수 없음 - Participants: 최소 2명이며, 이름은 중복될 수 없음
- Participants와 LadderResults를 인덱스 경로(Map)를 기준으로 매핑하는 로직 구현 - 특정 참여자의 이름을 검색하여 결과를 반환하는 기능 추가
- 'all' 입력 전까지 반복해서 결과를 조회하는 runInquiry 로직 구현 - 사용자 입력값에 따라 단일 결과 또는 전체 결과 출력 기능 연결를 반환하는 기능 추가
- 이름과 실행 결과의 가로 정렬을 위한 6칸 포매팅(%6s) 적용 - 사다리 기둥과 이름의 위치를 맞추기 위한 라인 시작 여백 처리 - 개별 결과 및 전체 결과 출력 메서드 추가
- 1~5자 사이의 이름 생성 성공 케이스 검증 - 입력값 앞뒤 공백 제거(trim) 로직 확인 - 빈 값(blank) 입력 시 예외 발생 및 메시지 검증 - 5자 초과 입력 시 예외 발생 및 메시지 검증
- 사다리 경로에 따른 결과 매핑 검증 - 존재하지 않는 이름으로 결과 조회시 예외처리 - 전체 결과 조회시 모든 결과 반환
- 2명 이상, 중복 불가에 대한 검증
- List<Line>을 관리하는 일급 컬렉션 Lines 클래스 추가 - Lines에 Iterable 구현 및 size, get 메서드 추가
- 동 클래스의 generateResults()와 동일 패키지의 접근까지 허요하여 테스트가 가능하도록 함
- 언어 차원에서 싱글톤 보장 - 객체보다는 상태임을 명시
- toList()로 반환하여 불변 객체로 보호
There was a problem hiding this comment.
안녕하세요 진형님, 리뷰가 늦어졌네요ㅜㅜ
우선 미션을 하나 드리려고 합니다. 지금 사용하고 있는 모든 정적 팩토리 메서드 코드들을 삭제하고, 외부에서 주입받는 형식으로 리팩토링을 진행해보시죠. 간단히 말하자면,
생성자는 외부 주입을 통해 생성만 해봅시다.
public class Points implements Iterable<Point> {
private final List<Point> points;
public Points(List<Point> points) {
this.points = points;
}
리팩토링을 진행하면서 막히는 부분이 있다면 말해주세요. 그리고 리팩토링 전, 후 코드를 비교해보면서 느낀점을 작성해주세요. ex) 정적 팩토리를 썼을 때의 장단점, 외부 주입을 했을 때의 장단점, 내가 앞으로 선택할 방식은?
두 번째로 Controller에 대한 답변을 드리려고 합니다.
이런 미션을 하기 전에는 Controller-Service-Repository의 구조에서만 개발을 하던 입장이라..
API 요청을 받아서 필요한 Service를 호출하는 역할로만 생각해왔습니다.
자체적으로 로직 수행이나 기능을 담지는 않고 로직을 담은 클래스들을 연결하는 책임으로 생각됩니다!
- 그러면 현재 코드에서는 api 요청 자체가 존재하지 않는데, controller는 불필요한거 아닐까요?? 아예 없애보시죠!
- 로직을 담은 클래스들을 연결하는 건, 별도의 Manager라는 Domain 객체를 만들어 위임해도 괜찮을 것 같네요.
해당 내용 관련해서는 제가 이전에 다른 리뷰이에게 달아놓은 리뷰가 있어서, 참고해보면 좋을 것 같아요.
next-step/java-lotto-clean-playground#151
두 작업 모두 시간이 꽤 걸리는 리팩토링 작업일 수도 있지만, 꼭 해보는걸 추천드립니다! 어차피 마지막 미션이니 늦게 리뷰 요청을 해도 상관 없어요ㅎ_ㅎ 파이팅입니다!
| return IntStream.range(0, width) | ||
| .boxed() | ||
| .collect(Collectors.toMap( | ||
| index -> index, | ||
| this::climb, | ||
| (oldValue, newValue) -> newValue, | ||
| LinkedHashMap::new | ||
| )); |
There was a problem hiding this comment.
이건 반복문 쓰는게 더 코드가 직관적일 것 같은데요??
public Map<Integer, Integer> generateREulsts2() {
Map<Integer, Integer> results = new LinkedHashMap<>();
for (int index = 0; index < width; index++) {
results.put(index, climb(index));
}
return results;
}
스트림을 선택한 이유가 있었나요??
| int climb(int startIndex) { | ||
| return lines.move(startIndex); |
| public List<Point> getValues() { | ||
| return Collections.unmodifiableList(points); | ||
| } |
There was a problem hiding this comment.
Collections.unmodifiableList를 사용하면 모든 경우에서 불변을 유지할 수 있는걸까요??
사다리 게임 리팩터링
1. 객체의 책임 분리 및 일급 컬렉션 적용
2. 의존성 주입(DI)을 통한 결합도 해제
3. 네이밍 변경
4. Enum 적용