개발자는 기록이 답이다
우테코 6기 프리코스 1주차 회고 본문
최근 들어 클린코드라는 개념에 관심이 생겼고, 좀 더 좋은 코드를 만들기 위해 어떤걸 배워야할지 고민하던 중이었습니다.
그래서 이번에 우아한 테크코스 6기 신청을 하게 되었습니다.
우테코 프리코스는 4주간 주어진 미션의 요구사항대로 프로그래밍을 역량을 확인하는 심사과정 중 하나입니다
첫번째 몰입에 대한 자기소개서, 두번째 4주간 미션을 진행하고 동기분들과 코드리뷰를 통해 점점 성장하는 모습을 보여줘야 합니다
합격 여부를 떠나서 프리코스 기간에도 다른 분들이랑 코드 리뷰하면서 배울 점이 많아서 좋은 경험인것 같습니다 :)
숫자 야구 게임
woowacourse-precourse 원격 저장소에서 git fork를 한 후 clone해서 미션을 진행한 후 pr를 날리는 방식이었습니다.
🚀 기능 요구 사항
기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다.
- 같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 낫싱이란 힌트를 얻고, 그 힌트를 이용해서 먼저 상대방(컴퓨터)의 수를 맞추면 승리한다.
- 예) 상대방(컴퓨터)의 수가 425일 때
- 123을 제시한 경우 : 1스트라이크
- 456을 제시한 경우 : 1볼 1스트라이크
- 789를 제시한 경우 : 낫싱
- 예) 상대방(컴퓨터)의 수가 425일 때
- 위 숫자 야구 게임에서 상대방의 역할을 컴퓨터가 한다. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다. 게임 플레이어는 컴퓨터가 생각하고 있는 서로 다른 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과를 출력한다.
- 이 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임이 종료된다.
- 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.
- 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.
숫자 야구 게임을 시작합니다.
숫자를 입력해주세요 : 123
1볼 1스트라이크
숫자를 입력해주세요 : 145
1볼
숫자를 입력해주세요 : 671
2볼
숫자를 입력해주세요 : 216
1스트라이크
숫자를 입력해주세요 : 713
3스트라이크
3개의 숫자를 모두 맞히셨습니다! 게임 종료
게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.
1
숫자를 입력해주세요 : 123
1볼
...
미션은 기능 요구사항, 프로그래밍 요구 사항, 과제 진행 요구 사항 3가지를 만족시켜야 통과할 수 있습니다.
특히 기능을 구현하기 전에 docs/README.md에 구현할 기능 목록을 정리해야 하는 것이 우선이었습니다.
원래의 저는 생각보다 행동이 앞서서 구현하면서 아니다 싶은건 수정해나갔었는데, 기능 목록 단위 docs를 제일 먼저 커밋해야 했기 때문에 어떻게 설계하면 좋을지 먼저 생각해야 했습니다!
여기서 설계라고 하시면 의문이 드시는 분들도 계실거에요!
"오잉? 그냥 코딩테스트 풀듯이 풀면 되는거 아니야?"
저 역시도 그렇게 생각했었고, 기능 요구사항만 흘깃 보고 백준 알고리즘처럼 Scanner를 사용해서 문제를 풀면 되는 줄 알았는데,
프로그래밍 요구 사항에 나와있는 Java 코드 컨벤션을 확인했을때, 클래스를 사용해서 문제를 구현하는 거라고 나와있어서 당황했습니다.
기존의 저는 기계처럼 클래스 만들고, 생성자만들고, getter,setter를 만들곤 했었는데요.
DB랑 연관 짓는 것도 아니고 순수 자바 코드로만 객체를 분리해서 구현한다는 것이 어렵게 느껴졌습니다.🥵
그래서 구글링해서 이전 기수분들 코드 참고하면서 객체를 분리시켰고, 다른 사람 코드보면서 배운다는게 이런거구나 느낄 수 있었습니다!
UML 다이어그램
객체끼리의 연관성을 알기 위해 UML 다이어그램을 docs에 추가했는데 리뷰어님들 반응이 너무 좋더라구요!
사실 연관관계를 맞게 설정했는지, 마름모인지 화살표가 왼쪽인지 오른쪽인지 100% 확실하진 않지만,
이렇게 UML다이어그램을 통해 리뷰어분들도 쉽게 접근할 수 있어서 좋다는 피드백을 받았습니다!
앞으로는 모두의 좋은 말씀을 해주신 것에 부응하기 위해 UML 다이어그램 연관관계 설정할때 좀더 확실히 알고 사용해야 할 것 같습니다!
(상속, 집합, 의존, 인터페이스와 실체화..)
+------------------+ +----------------------+
| Number | | Player |
+------------------+ +----------------------+
| - number: int |<>----<| numbers: List<Number>|
+------------------+ +----------------------+
|
|
|
^
+----------------------------+ +---------------------+
| Computer | | GameState |
+----------------------------+ +---------------------+
|- answerNumbers:List<Number>|--<>-----| RUNNING,END,RESTART |
|- gameState: GameState | | |
+----------------------------| +---------------------+
|
|
v
+-------------------+
| GameResult |
+-------------------+
|- strikeCount |
|- ballCount |
+-------------------+
디렉토리 구조(객체 분리)
객체 분리에 관한 것은 '오리님' 코드 보면서 공부했습니다! 😆
메소드 분리를 할때 객체가 가져야할 메소드까지 생각하면서 고려하신 코드였기 때문에, 객체 지향적이다는게 어떤것인지 느낄 수 있는 깊이있는 코드였습니다.
또한 메소드 내부에서 한 가지일만 하도록 SRP원칙을 잘 지켜주셨기 때문에 메소드 분리하는 과정에서도 많이 배울 수 있었습니다.
사용자가 입력한 숫자 3개를 받아서 컴퓨터의 숫자 3자리와 일치하는지 점검해야 하는 부분에서 Number클래스를 객체로 사용해서 Computer객체와 Player객체에서 List로 사용할 수 있게끔 만들었는데, 이를 '일급 컬렉션'이라고 부릅니다.
이렇게 하면 Number클래스가 컬렉션을 Wrapping하고 관리하게 되므로, 캡슐화와 코드의 가독성, 유지보수성, 테스트 용이성을 높일 수 있다고 합니다.
일급 컬렉션(First-Class Collection)
- 프로그래밍에서 컬렉션(리스트, 배열, 맵 등)을 객체의 속성으로 사용하거나 메서드의 매개변수로 전달할 수 있는 경우를 가리킵니다.
- 여기서 "일급"이라는 용어는 이러한 컬렉션들이 다른 객체와 동등한 지위를 가지며 중요한 역할을 할 수 있음을 나타냅니다.
src
├─ main
│ └─ java
│ └─ baseball
│ ├─ domain
│ │ ├─ computer
│ │ │ ├─ Computer.java
│ │ │ └─ GameState.java
│ │ ├─ game
│ │ │ ├─ BaseBallGame.java
│ │ │ ├─ GameMessage.java
│ │ │ └─ GameResult.java
│ │ ├─ number
│ │ │ └─ Number.java
│ │ └─ player
│ │ └─ Player.java
│ ├─ exception
│ │ ├─ computer
│ │ │ ├─ ComputerEndStateRefreshException.java
│ │ │ └─ GameStateNotFoundException.java
│ │ ├─ game
│ │ │ └─ GameResultTotalCountException.java
│ │ ├─ number
│ │ │ └─ NumberOutOfRangeException.java
│ │ └─ player
│ │ ├─ DuplicateNumberException.java
│ │ └─ InputSizeException.java
│ └─ Application.java
└─ test
└─ java
└─ baseball
├─ domain
│ ├─ computer
│ │ ├─ ComputerTest.java
│ │ └─ GameStateTest.java
│ ├─ number
│ │ └─ NumberTest.java
│ ├─ player
│ │ └─ PlayerTest.java
│ └─ GameResultTest.java
└─ ApplicationTest.java
For문에서 Stream으로 변경하면서 가독성 향상
아래는 pull request에서 코드 리뷰 받은 내용입니다.
gameResultMessage()라는 메소드가 여러개의 분기처리로 한가지 기능 이상을 하고 있다고 생각하고 있었고,
다음번에는 메소드를 분리해야겠다고 생각하고 있었는데, 코드리뷰 스터디원분께서 stream으로 리팩토링한 코드를 제시해주셨습니다.
stream과 람다를 사용해서 각 조건과 메세지를 한눈에 파악하기 쉽게 만들어서 가독성을 향상시킨 코드 같습니다!
조건문을 추가하려면 스트림에 새로운 SimpleEntry를 추가하기만 하면 되서 간단한것 같습니다.
하지만 저같은 stream 초보자는 바로 읽고 이해하기가 어려워서 stream에 대한 꾸준한 공부도 필요할 것 같습니다.
public static void gameResultMessage(GameResult gameResult) {
if (gameResult.isNothing()) {
System.out.println(GAME_RESULT_NOTHING_MESSAGE);
return;
}
if (gameResult.ballCount() == 0) {
System.out.printf(GAME_RESULT_STRIKE_MESSAGE + LINE_BREAK, gameResult.strikeCount());
return;
}
if (gameResult.strikeCount() == 0) {
System.out.printf(GAME_RESULT_BALL_MESSAGE + LINE_BREAK, gameResult.ballCount());
return;
}
System.out.printf(GAME_RESULT_BALL_MESSAGE + BLANK + GAME_RESULT_STRIKE_MESSAGE + LINE_BREAK, gameResult.ballCount(),
gameResult.strikeCount());
}
public static void gameResultMessage(GameResult gameResult) {
Stream.of(
new SimpleEntry<>(gameResult::isNothing, GAME_RESULT_NOTHING_MESSAGE),
new SimpleEntry<>(gr -> gr.ballCount() == 0, String.format(GAME_RESULT_STRIKE_MESSAGE, gameResult.strikeCount())),
new SimpleEntry<>(gr -> gr.strikeCount() == 0, String.format(GAME_RESULT_BALL_MESSAGE, gameResult.ballCount())),
new SimpleEntry<>(gr -> true, String.format(GAME_RESULT_BALL_MESSAGE + BLANK + GAME_RESULT_STRIKE_MESSAGE, gameResult.ballCount(), gameResult.strikeCount()))
)
.filter(entry -> entry.getKey().test(gameResult))
.findFirst()
.ifPresent(entry -> System.out.println(entry.getValue()));
}
'우아한테크코스' 카테고리의 다른 글
우테코 6기 프리코스 4주차 회고 (0) | 2023.11.15 |
---|---|
우테코 6기 프리코스 3주차 회고 (0) | 2023.11.15 |
우테코 6기 프리코스 2주차 회고 (1) | 2023.11.01 |