[그리디] 김민욱 사다리 미션 1~5 단계 제출합니다#95
Conversation
… 간격으로 정렬하여 출력하는 printNames, printRewards 로직 추가.
…Code 메서드 오버라이딩 추가.
2Jin1031
left a comment
There was a problem hiding this comment.
민욱님 고생많으셨습니다~
시험 끝나자마자 빠르게 미션하느라 아쉬운 점도 많을 것 같아요. 그럼에도 많은 고민 해주셨네요~ 👍👍
로또 미션에서도 리뷰를 드렸던 터라 흐름이 더 잘 보이는데요, 원시값 포장을 적용해보려는 시도가 잘 느껴져서 좋았습니다 ㅎ.ㅎ
다만 Readme도 신경 써주시면 좋겠어요!
그리고 도메인 테스트는 잘 작성되어 있는데, 컨트롤러 쪽은 테스트가 없는 것 같아요.
작성하지 않으신 이유가 있을까요? 만약 방법이 익숙하지 않아서였다면, 이번 마지막 자바 미션에서 꼭 한번 연습해보시면 좋겠습니다!
코멘트 몇가지 남겨놨으니 한번 확인해보시고 추가적인 질문이 있다면 마구마구 요청해주세요
뜨거워도 좋고 뜨겁지 않아도 좋으니 즐겁게 리펙해주세요! 화이팅 🔥💧🔥💧
| @Test | ||
| @DisplayName("제어된 제너레이터를 통해 빈 공간 발생 시 사다리가 재생성되는지 검증한다.") | ||
| void generateAndRetryWhenEmptyInterval() { | ||
| BooleanGenerator customGenerator = new BooleanGenerator() { |
There was a problem hiding this comment.
new BooleanGenerator() {...} 방식으로 테스크 코드를 작성해주셨네요~
이렇게 작성해주신 이유가 궁금해요!
There was a problem hiding this comment.
익명 클래스를 사용하여 generate() 메서드를 오버라이딩하고 반환값을 의도한 대로 제어했습니다. 이를 통해 '사다리 라인이 연속으로 비어있는 상황'이나 '순차적으로 번갈아 생성되는 상황' 등 특수한 시나리오를 강제로 재현하고 테스트를 진행하고자 했습니다!
There was a problem hiding this comment.
익명 클래스를 사용해주셨군요~ 사용해보시는 건 제가 처음 봤네요!
좋아요 익명 클래스 라는 것도 일회성으로 사용되고 말 클래스를 구현하기 위해 탄생한 개념이기 때문에 테스트에서만 필요한 클래스를 만들어야 하는 지금 상황에서 적절하다고 생각해요~!
지금껏 테스트 코드에 대한 코멘트를 별로 남겨보지 않았던 것 같아서 하나의 코멘트를 남겨볼까해요.
현 코드는 callCount > 6 라는 내부 호출 횟수에 의존하고 있어서,
- 사다리 크기(폭 4, 높이 2)와 호출 횟수(6)의 관계를 타개발자가 직접 계산해야 테스트 의도를 이해할 수 있음
- "빈 공간 발생 → 재생성"이라는 행위를 검증하는 게 아니라, 특정 호출 패턴을 시뮬레이션하는 데 집중하고 있음
이라는 생각이 들었습니다
어떻게 하면 테스트하고자 하는 목적을 달성하면서 위의 두가지를 해결할 수 있을까에 대한 고민을 해봤어요.
그러면 빈 사다리 발생 -> 재생성 을 테스트하는 방향성이 크게 두가지 있을 것 같더라고요.
- 행위 검증 -> "빈 공간이 나오면 재생성을 시도한다"
- 관심사: generate의 내부 전략
- 결과 기반 -> "빈 간격이 없는 사다리가 반환된다"
- 관심사: 결과값
지금 테스트 코드를 작성하기는 제가 너무 늦게 코멘트를 달아버린 것 같아서,
생각만 달아주셔도 좋을 것 같아요.
민욱님이 생각하시기에, 두가지 방향성 중에 지금 상황에서 어떤 것을 검증해야 한다고 생각하시나요?
두 가지 방식을 분리해서 검증해야 하는 이유는 무엇일까요?
민욱님이 선택하신 방식은 어떻게 검증할 수 있을까요?
너무 어렵다면 저에게 역질문 주셔도 좋습니다~
| } | ||
|
|
||
| public boolean hasName(String targetName) { | ||
| return this.name.getValue().equals(targetName); |
There was a problem hiding this comment.
getter로 객체 내부의 값을 꺼내서 Player가 직접 비교하고 있는데요,
PlayerName 입장에서 보면 외부인 Player가 자신의 값을 꺼내 비교하는 셈이 되지 않을까요?
혹시 민욱님은 이 부분에 대해 어떻게 생각하시나요!?
There was a problem hiding this comment.
처음 저는 Player가 PlayerName의 값을 꺼내와서 자바에서 제공하는 equals API로 비교해도 문제가 없다고 생각했습니다.
그동안 객체 내부의 값을 반환하는 것을 지양해야 하는 주된 이유가 '외부에서 값을 수정할 위험' 때문이라고 생각했는데, 안전한 자바 API와 불변 객체를 사용한다면 그 위험을 피할 수 있다고 판단했기 때문입니다. 하지만 핵심은 값의 변질 여부가 아니라, 객체의 책임과 자율성에 관한 문제였습니다!
리뷰어님의 말씀대로 Player라는 외부 객체가 PlayerName 내부의 값을 직접 꺼내어 비교하는 것은 객체의 자율성을 지키지 못한 방식이라고 생각합니다.
이를 리팩토링한다면, Player는 비교를 요청하기만 하고 직접적인 비교 로직은 값을 가지고 있는 PlayerName 스스로가 처리하는 방식으로 리팩토링을 진행할 수 있겠습니다 !
There was a problem hiding this comment.
오 좋습니다~~ 맞아요 제가 이야기 하고 싶었던 부분은 객체의 책임에 대한 이야기였습니다.
Player는 비교를 요청하기만 하고 직접적인 비교 로직은 값을 가지고 있는 PlayerName 스스로가 처리하는 방식
이렇게도 풀 수 있겠네요~
그럼 제가 처음에 코멘트를 남긴 방향성부터 말씀드려볼게요!객체의 자율성 이야기가 조금 추상적으로 느껴질 수 있을 것 같아서, 좀 더 현실적인 이유를 들어볼게요. 저는 PlayerName이라는 값 객체를 만든 이유 자체가, "이름"이라는 개념에 단순한 문자열 이상의 규칙이 붙을 수 있기 때문이라고 생각해요. 예를 들면 이런 상황들이요.
- 대소문자 구분 안 함
- 앞 뒤 공백 무시
- 등등
그래서 지금은 비교가 equals로 끝나지만, 나중에 "공백을 무시하고 비교해줘"라는 요구사항이 생겼다고 가정을 해볼게요!
getter 방식이라면
this.name.getValue().equals(targetName)이 코드를 쓰는 모든 곳에서 .trim()을 붙이려고 수정을 해야 하는 일이 생길거에요!
만약 비교 메서드가 PlayerName 내에 존재한다면:
this.name.equals(new PlayerName(targetName))
// 또는
this.name.matches(targetName)규칙이 바뀌더라도 PlayerName 안의 메서드 하나만 고치면 될 거에요!
가정한 시나리오가 그렇게 현실적이지 않을 수도 있겠지만, 결국 PlayerName이라는 값 객체를 만든 이유는 "이름은 그냥 String이 아니라 자기만의 규칙을 가진 개념이다"라고 선언하고 싶은 거라고 생각해요. 근데 비교할 때마다 getValue()로 String을 꺼내버리면, 객체에 데이터만 있고 행동은 없는 상태가 되는 것 같아요.
[추가 리펙토링 방향]
이미 민욱님이 충분히 같은 방향성으로 이해해주셨다고 생각해요!
다만 조금 욕심내서 리팩터링 방향성에 대한 코멘트를 남겨본다면, 지금 비교 방식이
new PlayerName(targetName)라는 것을 확인했어요!
지금 확인하고 싶은게 이름이 같은 지 아닌 지 단순 boolean 값일 건데, 만일 생성자에 있는 검증 로직을 돌 수 도 있겠다는 생각이 들더라고요.
그러면 false라는 반환값이 아니라 예외가 터질 수도 있고요!
또한, PlayerName이라는 값 객체가 현재 하나의 필드만을 요하고 있는데, 값 객체를 생성하고자 하는 상황에서 추가적인 필드도 필요한 상황이 있다면 이 비교 메서드도 수정해야 하는 상황이 올 거라는 생각이 들었어요.
상황을 예로 들어보면 언어 정보도 필요하다고 가정을 해볼게요!
PlayerName(name, locale) 이렇게 될 거에요.
그러면 이름 문자열이 같은지만 묻고 싶었던 건데, locale 정보까지 신경 써야 하는 상황이 와요. 비교 행위가 PlayerName의 내부 구조에 종속되어 버리는 거죠.
반면 PlayerName 안에 비교 메서드를 두면:
// PlayerName 내부
public boolean matches(String input) {
return this.value.equals(input); // 내부 사정은 PlayerName이 알게끔
}필드가 늘어나도 외부 코드는 영향을 받지 않을 거라는 생각이 들었습니다!!
| } | ||
|
|
||
| public void validateMatch(Rewards rewards) { | ||
| if (this.names.size() != rewards.size()) { |
There was a problem hiding this comment.
Players가 Rewards의 크기를 검증해주고 있네요. 이것이 Players의 책임일까요??
There was a problem hiding this comment.
Players 라는 클래스가 Rewards 의 크기를 검증함으로써 단일한 책임을 지고 있지 않기 때문에 단일책임 원칙을 위배했다고 생각합니다!
Rewards 크기 검증에 대한 역할을 다른 곳에서 진행해보도록 하겠습니다 !
There was a problem hiding this comment.
(이 코멘트를 제가 빠뜨렸네요 😅)
리팩터링하신 부분 확인해 보니, players.size() != rewards.size() 검증이 컨트롤러에 위치해 있더라고요. 컨트롤러는 입출력 흐름을 조율하는 역할이라, 도메인 규칙이 이곳에 자리 잡으면 아래와 같은 상황이 생길 수 있을 것 같아요.
- 다른 진입점에서 게임을 구성할 때 동일한 검증을 중복 작성하게 됨
- 검증 누락 위험 증가
제가 처음 남긴 코멘트의 방향성이 다소 모호했던 것 같다는 생각이 들었어요.
당시에는 Players가 자신의 참가자 목록만 알면 되고, Rewards의 존재까지는 알 필요가 없다는 생각으로 코멘트를 남겼었어요. 그런데 그렇게 가면 Players의 size를 알아야 하는 주체가 컨트롤러도, Players 클래스도 아니게 되니 풀어가기가 까다로워지더라고요.
그래서 제가 떠올렸던 방향성은, 두 객체를 모두 알고 있는 상위 객체의 조합 단계에서 유효성 검증을 수행하는 것이었어요~ 이렇게 가면 '사다리를 타서 결과를 만든다'는 게임 진행 로직도 자연스럽게 상위 객체에서 함께 다룰 수 있을 것 같더라구요! 혹시 이 방향성에 대해서는 민욱님은 어떻게 생각하시는 지 궁금합니다!
| import java.util.List; | ||
|
|
||
| public class Players { | ||
| private final List<PlayerName> names; |
There was a problem hiding this comment.
Players라는 클래스 이름에서 저는 List를 묶은 일급 컬렉션을 예상했는데, 실제로는 List으로 구성되어 있네요.
Player가 아닌 PlayerName으로 구성하신 이유가, Player 생성 시 Reward가 필수적으로 필요하기 때문이라고 이해했는데요—그러면 Player가 생성되는 시점에 Reward가 반드시 있어야 할까요?
There was a problem hiding this comment.
사다리 매칭 작업은 LadderResult(시작 및 도착 위치), Players(참가자 명단), Rewards(보상 목록)를 조합하여 수행됩니다. LadderResult의 인덱스를 이용해 Players와 Rewards에서 각각 해당하는 값을 꺼내어 한 사람과 하나의 결과물을 매칭합니다.
따라서 Player 클래스는 이 매칭이 끝난 후 최종 결과만을 저장하는 데이터 객체 역할을 하므로, 객체가 생성되는 시점에 Reward 가 반드시 필요합니다.
또한 Players 클래스는 게임 로직 수행 전 참여자의 이름만을 관리하는 일급 컬렉션이므로, 매칭된 결과(Reward)를 가져야만 하는 Player 대신 PlayerName 으로 구성하게 되었습니다!
|
|
||
| public class PlayerName { | ||
| private static final int MAX_LENGTH = 5; | ||
| private final String value; |
There was a problem hiding this comment.
Position에서는 equals / hashcode가 잘 구현되어 있음을 확인했어요! 다만 이 클래스, PlayerName에는 equals / hashcode가 구현되어 있지 않네요~ 둘의 설계 방식이 다른데 왜 그렇게 다르게 구현했는 지 설명해주실 수 있나요? 만일 단순 빼먹은 거라면 equals / hashcode가 없는 PlayerName에서 어떠한 상황이 초래할 수 있기 때문에 구현해야 하는 걸까요?
There was a problem hiding this comment.
제가 해당 코드 작성을 누락했습니다...
아무래도 생소한 개념이다 보니 누락해서 제출했지만 다음 미션부터는 꼼꼼하게 작성해보도록 하겠습니다 ! ...
PlayerName 에서 equals / hashcode 를 구현해야하는 이유는 다음과 같습니다!
- 동일성과 동등성의 분리
동일성 : 자바에서 new PlayerName("이름")를 두 번 실행하면 메모리상에 완전히 독립된 2개의 객체가 생성됩니다. 자바의 기본 equals()는 이 메모리 주소를 비교하므로 둘을 다르다고 판별합니다. => 같은 이름을 가진 서로 다른 사람 : 동명이인
동등성 : 사다리 게임 도메인에서는 이름표에 적힌 글자(value)가 같다면 같은 사람으로 취급해야 합니다. 메모리 주소가 다르더라도 내부 값이 같으면 true를 반환하도록 논리적 동등성을 부여하는 작업이 equals() 재정의입니다. => 한 사람
- hashCode의 동반 필수성
equals만 재정의하고 hashCode를 재정의하지 않으면, HashSet이나 HashMap 같은 해시 기반 자료구조를 사용할 때 중복 검증이나 데이터 검색을 실패하게 됩니다. 메모리 주소 기반의 엉뚱한 해시코드를 반환하기 때문입니다.
그렇다면 equals / hashcode 를 사용하지 않았다면 생길 수 있는 문제점은 다음과 같습니다!
- 동명이인 참가자의 허용
까지만 알겠고 더 다른 내용이 있지만 완벽하게 이해가 되지 않아서 쫌 더 연구해보고 다시 코멘트를 남기겠습니다 !
There was a problem hiding this comment.
equals / hashcode 구현 이유의 두가지를 정확하게 짚어주셨네요
동명이인 참가자의 허용
제가 생각하는 방향성도 이와 동일합니다~
- equals / hashcode 를 구현하지 않으면 동명이인 참가자를 허용하는 시나리오
- equals / hashcode 를 구현하면 동명이인 참가자를 허용하지 않는 시나리오
그래서 민욱님이 어떤 시나리오를 염두에 두셨는지에 따라, 이게 코드 작성의 누락이 아닐 수도 있어요.
제가 코멘트를 단 이유는, 구현하지 않은 설계에 의도가 있었던 것인지 아닌지가 궁금해서였어요. equals / hashCode 구현 여부를 스스로의 기준에 따라 고민하고 설계할 수 있는지를 고민해볼 수 있는 미션이라 생각했기 때문이에요~
또한 이 부분은 스터디 시간에도 추가로 질문해주신 덕분에 한 번 더 질답이 오갈 수 있었고, 저도 민욱님이 어디서 헷갈리시는지 알 수 있어서 좋았어요 ㅋㅎㅋㅎ 저도 똑같이 헷갈렸던 지점이라 참 반가웠습니다.
앞으로도 더더 질문해주세요~
까지만 알겠고 더 다른 내용이 있지만 완벽하게 이해가 되지 않아서 쫌 더 연구해보고 다시 코멘트를 남기겠습니다 !
또 어떤 부분이 있을랑가요~~
만일 찾으면 저도 알려주세요 ㅋㅋㅋ 아니라면 꼭 다시 안 다셔도 되어요~ ㅎ.ㅎ
|
|
||
| @Test | ||
| @DisplayName("주입된 불리언 제너레이터의 반환값에 따라 사다리 라인이 검증된다.") | ||
| void generateWithSequentialRandom() { |
| package domain; | ||
| public class Reward { | ||
| private static final int MAX_LENGTH = 5; | ||
| private final String value; |
| return new Line(points); | ||
| } | ||
|
|
||
| private static boolean addPoint(List<Boolean> points, boolean previous, boolean above, BooleanGenerator gen) { |
There was a problem hiding this comment.
L14에서는 BooleanGenerator 의 변수명을 generator로 적어주셨는데, 여기서는 gen으로 축약해서 적어주셨네요. 같은 매게인자인데도 불구하고 네이밍이 다른 이유는 무엇인가요? 축약형은 지양하자가 초반 요구사항이었던 것 같아서요.
그리고 추가로 궁금한 점이 있어요. 축약형은 왜 지양해야 할까요?
There was a problem hiding this comment.
축약형으로 써서 제출했던 것은 제 부주의였습니다...
축약형 사용을 지양하고 학습 초기부터 잘못된 습관을 바로잡아야 하는 이유는, 시간이 지나면 코드를 작성한 사람이나 읽는 사람 모두 그 의미를 파악하기 어렵기 때문입니다.
여러 개의 변수명이 전부 축약형으로 적혀 있다면 코드를 이해하는 데 불필요한 시간과 노력을 쏟아야 하므로, 학습 초기부터 이런 잘못된 습관을 반드시 바로잡아야 한다고 생각합니다!
There was a problem hiding this comment.
시간이 지나면 코드를 작성한 사람이나 읽는 사람 모두 그 의미를 파악하기 어렵기 때문입니다.
저랑 같은 생각을 가지고 계시는 군요~
추가적으로 제가 생각하는 다른 이유는
축약형에 대한 컨벤션이 명확히 존재하지 않으면, 각자마다 원하는 축약형 방식으로 네이밍을 작성하고
이를 이해하는 몫은 다른 개발자라고 생각하기 때문도 있어요
| List<Boolean> points = new ArrayList<>(); | ||
| boolean previous = false; | ||
| for (int i = 0; i < width.getIntervalCount(); i++) { | ||
| boolean above = previousLine.isConnectedAt(i); |
There was a problem hiding this comment.
above = true이면 다음 point가 생기지 않도록 하는 규칙을 넣어주신 것 같아요~
따로 요구사항에는 존재하지 않았던 것 같아서 민욱님이 생각하기에 이 규칙이 필요하다고 판단한 이유가 단순히 궁금했어요.
There was a problem hiding this comment.
극단적인 예시로
|[][][]|-----|[][][][]|
|-----|[][][]|[][][][]|
|-----|[][][]|------|
|-----|[][][]|[][][][]|
|-----|[][][]|[][][][]|
([]는 공백을 의미합니다)
이런 형태의 사다리는 문제가 있는 사다리의 모양이지 않을까? 생각했습니다.
문제라고 생각할 수 있었던 이유는 네이버 사다리 게임을 참고한 결과 같은 위치에 연속으로 가로줄이 배치되는 형태가 발견되지 않음을 확인했기 때문입니다!
따라서 이와같은 이유로 사다리 게임의 규칙을 추가했습니다!
There was a problem hiding this comment.
명시된 요구사항뿐 아니라, 도메인 특성을 직접 파악해서 규칙까지 추가해 주신거군요!
도메인에 대한 이해도가 잘 드러나는 부분이라 좋네요 👍
| } | ||
|
|
||
| private static boolean determineNext(boolean previous, boolean above, BooleanGenerator gen) { | ||
| if (previous || above) { |
There was a problem hiding this comment.
이 변수명을 이렇게 처음 봤을 때는 이전, 위 라는 맥락뿐이어서 이해하는 데 다소 시간이 걸렸던 것 같아요. 좀 더 의미 있는 변수명을 사용해보는 건 어떨까요?
There was a problem hiding this comment.
네! 맞습니다!
코드는 여러 개의 변수들이 모여서 하나의 큰 흐름을 이룬다고 생각합니다. 그 흐름을 이해하기 위해서는 변수명의 설정이 무엇보다 중요합니다. 만약 변수명이 단순한 영어 단어 하나라면, 그 변수가 의도하는 바를 명확하게 전달하기 어려울 것이라는 점을 깨달았습니다.
앞으로는 단순한 영어 단어의 나열이 아니라, 다른 사람이 코드를 읽었을 때 그 의미를 한 번에 납득할 수 있는 명확한 변수명을 짓도록 의식적으로 노력하겠습니다!
리뷰어님의 소중한 시간을 뺏어가서 죄송합니다...
There was a problem hiding this comment.
제가 남긴 코멘트를 다시 읽어보니,
정답이 정해져 있지 않은 어려운 주제인 만큼 제 의도를 좀 더 명확하게 담았어야 했다는 아쉬움이 남네요.
사실은 민욱님이 어떤 기준으로 네이밍을 하시는지 궁금했던 건데, 정작 그렇게 질문을 드리지는 못했더라고요. 그래도 스터디 자리에서나마 이에 대한 생각을 들어볼 수 있어서 좋았습니다!
민기님 말씀처럼,
저도 민기님도 여전히 고민하고 있는 부분이고,
'명확한 네이밍'에 대한 기준은 사람마다 다를 수밖에 없는 것 같아요.
그래서 자기만의 기준이 잘 서 있다면 어떤 상황에서도 흔들리지 않고 풀어갈 수 있을 거라 생각합니다!
|
리드미 누락에 대해서는 변명의 여지가 없습니다... 다음 미션부터는 리드미까지 깔끔하게 작성해서 제출하도록 하겠습니다! 컨트롤러에 대한 테스트는 리뷰를 반영한 후 하고 싶었습니다 ! 리팩토링을 하는 과정에서 로직이 수정 될 수 있기 때문에 초기 단계에서 컨트롤러의 테스트 코드를 작성하는 것이 두렵습니다! 빠르게 리팩토링을 한 뒤에 다시 뵙도록 하겠습니다! 인내를 가지고 참고 기다려주셔서 감사합니다 선생님 ㅜㅜ |
2Jin1031
left a comment
There was a problem hiding this comment.
민욱님 리뷰가 늦어져서 죄송합니다 ㅜㅜ 하다가 그대로 잠들어 버렸네요...
제가 코멘트 남긴 부분만 다시 한번 봐주시면 좋을 것 같습니다.
다음 미션도 있고, 공통 리뷰 파트는 스터디에서 이미 함께 이야기 나눴던 만큼, 답변만 확인하고 머지하셔도 충분할 것 같다는 저의 개인적인 생각이 있기는 하지만,
그래도 민욱님 입장에서 아쉬운 부분이 있다면 언제든지 편하게 말씀해주세요!
😄 소개
안녕하세요, 이진님! 그리디 백엔드 4기 김민욱입니다.
바쁘신 와중에 시간 내서 리뷰해주셔서 감사합니다!
이번엔 정말 뜨겁게 리팩토링을 해보도록 하겠습니다 !!
🚗 해당 코드의 단계적인 흐름 설명
참여자 이름 입력
실행 결과 입력
데이터 매칭 검증
사다리 높이 입력
결과 확인 대상 입력
💭 고민한 내용
1. 자료 구조의 선택: 2차원 배열 vs 일급 컬렉션
고민 과정 : 처음에는 사다리를 2x2 배열로 진행해야 하는지 고민이 많았습니다.
해결 방법 : 하지만 결국 객체를 포장하는 일급 컬렉션으로 진행해야 한다는 것을 깨달았습니다! 일급 컬렉션 내부의 객체 배열에 true, false 값을 집어넣고, 출력을 할 때 문제 형식에 맞게 문자열로 변환하여 출력하는 방식으로 해결했습니다.
2. 좌표 매핑 및 이동 검증 원리 (Line.move)
구현은 했지만 어떻게 대상에 사다리를 이동시켜서 값을 구할 수 있을까? 라는 고민을 했습니다 !
해결 방법은 다음과 같습니다 !
해결 방법 :
예를 들어 사람이 4명일 경우 세로 기둥의 Position 인덱스는 0, 1, 2, 3 으로 구성됩니다. 가로 한 줄에 도달했을 때, 현재 기둥의 인덱스를 기준으로 좌우 간격의 불리언 값을 검증하여 이동할 위치를 찾습니다!
왼쪽 다리 검증 (canMoveLeft) : 반환 값이 true 라면, 현재 위치 - 1 이 됩니다!
오른쪽 다리 검증 (canMoveRight) : 반환 값이 true 라면, 현재 위치 + 1 이 됩니다!
그대로 하강 : 양쪽 간격이 모두 false 라면 연결된 다리가 없으므로, 위치 값을 변경하지 않고 그대로 반환합니다.
4. Position 의 원시값 포장
로또 미션과 다르게 이번엔 숫자들을 하나씩 포장하여 원시값을 포장했습니다. 때문에 +1 이나 -1 같은 단순 산술 연산 로직을 외부에 노출하지 않고, moveLeft(), moveRight() 라는 명확한 도메인 행위로 객체 내부에 응집시킬 수 있었습니다.
5. 사다리 게임 분석과 방어 로직
사다리 게임에서의 규칙상 |-----|-----| 처럼 나란히 사다리가 생기는 것을 방지해야 하고, 바로 밑에 사다리가 생기는 것도 방지해야 했습니다.
고민했던 사항 : 처음에는 현재 층을 기준으로 아래층도 방어해야 하는지 의문이 들었습니다.
하지만 위에 층만 방어 로직을 구현한다면 나머지는 자동으로 아래층에도 중복으로 생기는 일이 없다는 것 을 깨달았습니다! 이를 바탕으로 determineNext 메서드에서 이전 상태 값들을 확인하여, 가로와 세로 모두 사다리가 겹치는 현상을 깔끔하게 방어할 수 있었습니다.
❓ 질문 사항
코드를 리팩토링함에 따라 궁금한 사항이 많이 생길 것 같습니다.
이번엔 사다리 미션 구현에 많은 시간을 투자하지 못한 만큼 리팩토링에 더 신경 써보도록 하겠습니다!!