졸프등등... 잡다한 일정으로... (저의...ㅇㅓㅂ ㅂ ㅗ....) 중도 포기했지만 이렇게 보내긴 아쉬워서 끄적이는 2주 차 회고록!임미다!
1. 파일 구성
computer 폴더 : Computer class를 호출하는 controller, 정답 baseball을 생성하는 computer
baseballGame 폴더 : 게임의 전체 로직을 수행하는 BaseballGame, user input값과 생성된 값을 비교하는 Result, Message
AppConfig : 설정 사항 및 게임 시작 함수
2. 코드 리뷰
좀 더 객체 지향적으로 구성하기 위해 노력해본 과제입니당..
과제에서 주어진 조건은 컴퓨터에서 baseball을 생성한 상태에서 user가 값을 유추해 내야 했습니다.
일단 computer class와 controller class를 생성하였습니다.
computer은 말 그래도 baseball을 생성하게 하는 클래스이고 controller는 computer을 호출하여 생성을 하게 하는 클래스입니다.
최대한 객체지향적으로 구현하려고 노력하였으나,
public class Controller {
private final Computer computer = new Computer();
~
}
이런 식으로 구현하여 구현에 너무 의존하였다는 생각이 들어 아쉬웠습니다.
computer 클래스에서는 최대한 함수에 하나의 역할을 부여하기 위해 노력했습니다.
public class Computer {
private final List<Integer> baseballs = new ArrayList<>();
# 생성한 baseball 반환
public int[] getBaseballs() {~}
# 볼 생성
public void makeBalls(int BALL_COUNT) {~}
# 중복 확인 후 baseballs에 add
public void insertBall(int ball) {~}
# baseballs에 이미 같은 번호가 있는지 확인
public Boolean isDuplicate(int ball) {~}
}
makeBalls에 모든 로직을 구현하는 것이 아닌 insertBall과 isDuplicate를 선언 함으로 써 생성과 입력과 중복확인의 책임을 분리해 주었습니다.
실제 게임 로직인 baseballGame 클래스를 생성하였습니다.
public class BaseballGame {
private static final String RESTART = "1";
private static final String STOP = "2";
private final int ball = 0;
private final int strike = 1;
private final int BALL_COUNT;
public BaseballGame(int BALL_COUNT) {
this.BALL_COUNT = BALL_COUNT;
}
public void play() {
printStartMessage();
do {
runGame();
} while (getAndCheckStatus());
}
public void runGame() {
int[] balls = new Controller(BALL_COUNT).getBalls();
do {
~
} while (!correct(result));
}
~
}
RESTART와 STOP을 하드코딩이 아닌 명시적으로 보여주기 위해 아래와 같이 상수 선언 후 사용하였습니다.
private static final String RESTART = "1";
private static final String STOP = "2";
Ball_count가 변경될 수 도 있다고 가정하여 생성자 주입으로 해당 변수의 값을 받아오게 해 주었습니다.
private final int BALL_COUNT;
public BaseballGame(int BALL_COUNT) {
this.BALL_COUNT = BALL_COUNT;
}
user input 값 받아 줄 때 원하는 형식이 아니라면 IllegalArgumentException 날려줍니다.
public String getUserBalls() throws IllegalArgumentException {
String input = getInput();
if (inputException(input)) {
throw new IllegalArgumentException("형식에 맞지 않음");
}
if (inputDuplicateException(input)) {
throw new IllegalArgumentException("중복 값 포함 됨");
}
return input;
}
이 부분에서는 input을 미리 저장해주는 게 아니라 받자마자 exception을 날릴 수 있다면 더 좋았을 것이라는 피드백도 받았습니다.
결과 값을 출력해주는 Result 클래스를 생성하였습니다.
public class Result {
public static int[] getResult(int[] balls, int[] userBalls) {
int strike = 0;
int ball = 0;
for (int i = 0; i < balls.length; i++) {
if (equal(balls[i], userBalls[i])) {
strike += 1;
} else if (contain(balls, userBalls[i])) {
ball += 1;
}
}
return new int[]{ball, strike};
}
public static Boolean equal(int ball, int userBall) {
return ball == userBall;
}
public static Boolean contain(int[] balls, int userBall) {
return Arrays.stream(balls)
.anyMatch(num -> num == userBall);
}
}
위의 computer 클래스를 보면 baseballs를 int []로 반환해줍니다.
당시 구현 중이었을 때는 배열 값이 변하지 않도록 int형 배열을 선언하였고 그래서 result 클래스에서 두 값을 비교해줄 때 equal과 cotain을 직접 구현하였는데 피드백에서는 단순히 List로 구현하고 있는 매소드를 사용하라고 하였습니다.
보통 이게 큰 의미가 없는 것 같기도 ... (좀 허탈했음 ㅋ)
game로직에서 출력해줘야 할 Message 클래스도 생성하였습니다.
private static final String startMessage = "숫자 야구게임을 시작합니다.";
private static final String restartMessage = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.";
private static final String inputMessage = "숫자를 입력해주세요: ";
private static final String nothingCaseMessage = "낫싱";
private static final String ballCaseMessage = "%d볼";
private static final String strikeCaseMessage = "%d스트라이크";
private static final String newline = "\n";
private static final String finishMessage = "게임 종료";
하드코딩을 하지 않도록 출력해야 할 문장들을 상수로 선언하여 주었습니다.
아래와 같이 해당 문장들을 출력할 수 있는 함수를 만들어 준 뒤 호출하여 사용하였습니다.
public static void printStartMessage() {
System.out.println(startMessage);
}
해당 클래스의 매소드들을 static으로 선언한 이유는 다음과 같습니다.
1. Message 클래스를 따로 생성하여 BaseballGame 클래스에 출력까지 맡기고 싶지 않았음
2. 공통 클래스라고 봐도 무방함. BaseballGame클래슬 더 쪼갰다면 Message는 공통 클래스로 선언하고 출력하는 것이 맞다고 판단함.
3. 하드코딩이 아닌 상수 선언으로 명확한 구현을 하고 싶었음
조건 값이 바뀌는 경우를 생각하였고 main은 단순하게 실행만 담당하게 하고 싶어서 AppConfig를 생성하였습니다.
public class AppConfig {
public static final int BALL_COUNT = 3;
public void startGame(){
BaseballGame baseballGame = new BaseballGame(BALL_COUNT);
baseballGame.play();
}
}
3. 총평
구현할 당시에는 로직에 대한 고민이 굉장히 컸습니다.
그냥 와르르 구현을 하면 분명 돌아가겠지만 그건 자바를 사용한 게 아닐 거라는 생각이 들어 호출부터 많이 고민했던 것 같습니다.
스프링 강의를 들을 때 당연히 이렇게 하면 되겠지 라는 부분이 자바로 과제를 구현하니까 왜 이게 당연했지? 아님 코테로 준비하던 파이썬의 로직을 생각해보며 이렇게 하면 되지 않을까? 유사한 매소드는 뭐가 있을까?를 생각해보며 과제를 해나갔던 것 같습니다.
이 과제에서 final과 static 그리고 static final에 대한 개념도 잡았고 매소드가 하나의 책임, 더 나아가 클래스가 하나의 책임을 가지게 하기 위해서는 어떻게 구현해야 하는지 진지하게 고민해본 시간이었습니다. (자바를 좀 더 공부해볼 필요를 느낀 순간이었다. 바본가 ㅋ)
우테코 자체가 업보라 이 과제를 하면서도 이걸 해.. 말아... 이랬지만 그래도 자바를 뭘 공부해야 할지 깨달을 수 있었습니다!
비록 내지 못한 과제지만 3주 차 리뷰도 곧 들고 오겠습뉘다!!
'📔 회고' 카테고리의 다른 글
[우테코 5기] 프리코스 1주차 온보딩 코드 리뷰 및 회고록 (4) | 2022.11.02 |
---|