이번 2주차 미션은 더욱 설계를 견고하게 하기로 다짐을 하고 시작했습니다.
이전의 설계를 세심하게 하지 않아 구현하며 많이 수정된 부분이 많았고 이로 인해 혼동이 많았습니다. 그래서 이번 회고에서는 설계 과정과 배운 점을 위주로 작성해보려 합니다!
초간단 자동차 경주 게임을 구현한다.
IllegalArgumentException
을 발생시킨 후 애플리케이션은 종료되어야 한다.경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
시도할 횟수는 몇 회인가요?
5
실행 결과
pobi : -
woni :
jun : -
pobi : --
woni : -
jun : --
pobi : ---
woni : --
jun : ---
pobi : ----
woni : ---
jun : ----
pobi : -----
woni : ----
jun : -----
최종 우승자 : pobi, jun
머리로만 익히는 것보다 이번 문제를 통해 실제로 적용시켜 보며 문제를 접근했습니다.
이러한 생각을 기반으로 각 객체가 무엇을 해야 하는지(Doing)와 무엇을 알아야 하는지(Knowing)를 나누어 정리해봤습니다.
Knowing: 입력 값 ( 시도횟수, 이름 )
Doing: 사용자의 입력을 받고, 적절한 형식으로 반환하는 역할
Knowing: 입력 값
Doing: 사용자의 입력이 유효한지 확인하는 역할
Knowing: 게임의 상태
Doing: 전체 흐름을 관리하는 역할
Knowing: 자신의 이름, 자신의 상태
Doing: 이동하는 역할
Knowing: 각 자동차의 이름과 상태
Doing: 자동차의 현재 상태를 관리하는 역할 (현재 위치와 이름)
Knowing: 자동차의 이동 과정
Doing: 각 자동차의 이동 결과를 기록하는 역할
Knowing: 이동을 위한 규칙
Doing: 규칙을 관리하는 역할
Knowing: 규칙, 자동차들
Doing: 규칙에 맞게 자동차에게 이동을 명령하는 역할
Knowing: 최종 이동 결과
Doing: 우승자를 판단하는 역할
Knowing: 우승자와 실행 결과
Doing: 우승자와 실행과정을 출력하는 역할
그래서 제가 생각한 입력 규칙은 아래와 같습니다!
[이름]
- 5자 이하여야 합니다. ex) pobii (x)
- 영어로만 이루어져야 합니다. ex) 우테코, pob2 (x)
- 중복된 이름은 사용할 수 없습니다. ex) woni, woni (x)
- 이름은 쉼표로 구분됩니다.
[시도 횟수]
- 숫자만 입력할 수 있습니다. ex) aa (x)
- 1부터 10까지만 입력할 수 있습니다. ex) 11 (x)
아래는 PR 링크입니다! 아직 부족한 부분이 많지만 리뷰는 언제나 환영입니다 🙇🏻♀️
출처:MVC 패턴
MVC 소프트웨어 디자인 패턴의 세 가지 부분은 다음과 같이 설명할 수 있습니다.
- 모델: 데이터와 비즈니스 로직을 관리합니다.
- 뷰: 레이아웃과 화면을 처리합니다.
- 컨트롤러: 모델과 뷰로 명령을 전달합니다.
이렇게 각각의 요소가 자신만의 역할을 수행하기 때문에, 특정 부분을 수정하거나 업데이트해야 할 때 관련된 역할에만 집중하면 됩니다. 이를 통해 코드의 유지보수가 쉬워지고, 역할이 명확히 나뉘어 더 체계적인 설계를 할 수 있습니다.
MVC 패턴을 이해할 때 도움이 된 블로그입니다! - MVC 패턴이 뭐야
전략 패턴은 알고리즘들의 패밀리를 정의하고, 각 패밀리를 별도의 클래스에 넣은 후 그들의 객체들을 상호교환할 수 있도록 하는 행동 디자인 패턴입니다.
public interface MoveStrategy {
boolean canMove();
}
public class RandomMoveStrategy implements MoveStrategy {
private static final int MIN = 0;
private static final int MAX = 9;
private static final int MOVE_THRESHOLD = 4;
@Override
public boolean canMove() {
int randomValue = Randoms.pickNumberInRange(MIN,MAX);
return randomValue >= MOVE_THRESHOLD;
}
}
저는 자동차의 이동 규칙을 유연하게 적용할 수 있도록 MoveStrategy
인터페이스를 통해 이동 여부를 결정하는 로직을 추상화했습니다. 이 방식은 추후 새로운 이동 규칙이 필요하거나 기존 규칙을 변경할 때 다른 코드에 영향을 주지 않으므로 유지보수가 용이하다는 장점이 있습니다. 또한, 전략 패턴 덕분에 자동차 외의 다른 객체가 이동 규칙을 필요로 할 때도 같은 구조를 재사용할 수 있습니다.
입력값을 검증하는 클래스를 따로 분리할지, 객체가 직접 검증하도록 할지 고민했습니다.
검증 클래스를 분리하면 검증 로직을 재사용하기 쉬워지고, 코드가 깔끔해지며 단일 책임 원칙을 충족하는 장점이 있습니다.
반면 객체가 직접 검증하게 되면, 해당 객체가 스스로 데이터의 일관성을 관리하므로 결합도가 낮아지고 유지보수에 유리할 수 있습니다.
단일 책임 원칙을 지키기 위해서 저는 입력값 검증 클래스를 분리해서 사용하기로 결정하였습니다.
시도 횟수를 검증할 때, 문자열로 받아 숫자로 변환할지, 숫자만 받도록 할지 고민했습니다.
최종적으로 문자열로 받는 방식을 선택했습니다. int
로 받을 경우 타입이 고정되어 안정적이지만, 문자열 입력 시 예외 처리가 필요합니다. String
으로 받을 경우 변환 과정이 추가되지만, 다양한 형식으로 입력한 값을 더 쉽게 처리할 수 있습니다.
저는 예외를 처리하는 과정이 가독성을 해칠 수 있다고 생각하여 문자열로 받는 것이 더 효과적일 것이라 판단했습니다.
테스트 코드 작성 시, 어느 범위까지 테스트를 수행해야 할지 고민했습니다.
모든 객체를 개별적으로 테스트해야 할지, 주요 로직과 상호작용을 중심으로 테스트를 구성해야 할지에 대한 고민이었습니다.
초기에는 각 객체의 메서드 단위로 테스트하는 것이 좋다고 생각했지만, 점차 객체 간의 협력이 중요한 만큼 주요 기능이 잘 작동하는지를 중심으로 통합 테스트를 강화하는 방향으로 방향을 잡았습니다. 이를 통해 단위 테스트와 통합 테스트를 적절히 조화시켜야 한다고 느꼈습니다.
이번 미션을 하면서 아쉬웠던 점은 객체들이 자신의 역할을 가지면서 협력하는 것을 목표로 하였지만 설계한대로 코드가 구현되었는 지에 대한 확신이 들지 않았습니다. 너무 역할과 책임, 협력에 초점을 둔 것이 아닐까하는 생각도 들었습니다.
이전 리뷰에서 '일급 컬렉션','불변 객체'에 대해 피드백을 받아 적용시켜보려 노력했지만, 아직 완전히 이해하지 못해서 부족한 부분도 있는 것 같습니다.
하지만 이러한 경험이 많은 것들을 배울 수 있는 좋은 기회였다고 생각합니다.
다음 미션에서는 위의 개념들을 학습하고 아쉬웠던 부분을 보완할 수 있도록 노력할 것입니다!
긴 글 읽어주셔서 감사합니다 😊
굉장히 많은 내용을 고민하고 적용하고자 하셨네요!
책까지 읽으면서 진지하게 고민하시는 부분이 인상깊었습니다.
흐름도도 잘 만들어주셔서 좋은 것 같아요!
mermaid를 활용하면 README에도 적용할 수 있을 것 같습니다.
남은 프리코스도 화이팅해서 꼭 우테코에서 뵈면 좋겠네요!
고생하셨습니다!
+) 감자 너무 귀엽네요.. ㅋㅋㅋㅋㅋ 🤣