이 글은 '프리코스' 과정에 작성한 글이며 현재 레포지토리에 있는 코드와 다를 수 있습니다. (리팩토링함)
🚩 4 주차 목표
1. 클래스(객체)를 분리하는 연습
2. 리팩터링
이 두 가지에 익숙해지는 것을 목표로 하고 있다.
[4주 차] 미션 - 다리 건너기 🪜
👉 미션 저장소
📆 기간: 2022.11.16(수) 15:00 ~ 2022.11.22(화) 23:59
1. 구현 과정
⚙️ 기능 구현 목록 작성
👉 기능 구현 목록
박굴렁쇠... 일단 코드가 돌아가게 구현해보자.
그런데 행복하지 않다.. 찝찝해
찝찝한 부분을 확인해보자.
1️⃣ 인스턴스 변수의 수를 줄이기 위해 노력한다.
현재 BridgeGameController에서 생성한 다리를 bridge라는 변수에 가지고 있고 내가 현재 가고 있는 bridge를 currentBridge, 시도한 횟수를 retryCount에 저장하고 있다.
필드(인스턴스 변수)의 수가 많은 것은 객체의 복잡도를 높이고, 버그 발생 가능성을 높일 수 있다. 필드에 중복이 있거나, 불필요한 필드가 없는지 확인해 필드의 수를 최소화한다.
- 우아한 테크코스 3주차 피드백 중
인스턴스 변수를 더 줄일 수 없을지 고민해보자.
2️⃣ 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.
함수(또는 메서드)가 한 가지 일만 잘하도록 구현하는 것은 중요한다. 아직 메서드 분리가 잘안되는 것 같다.
공부해보자!
🚀 새롭게 도전한 것들 (혹은 구현하면서 고려한 점)
1️⃣ 예외 처리
이전과 달리 잘못된 값을 입력했을 경우 다시 입력받는 조건으로 변경되었다.
그래서 이 부분을 다음과 같이 구현했다.
시도 1) while문 사용
while문을 활용해서 예외 발생 시 예외 발생 하지 않을 때 까지 반복하는 코드를 작성했다.
while문으로 코드를 반복하며 try문에서 예외가 발생하지 않았을 때 break하여 반복을 탈출하도록 구현했다.
시도 2) 재귀로 구현
현재 코드에서 depth를 줄이기 위해서 고민을 했다.
이때 입력을 받는 메서드가 있으면 catch문에서 해당 메서드를 재귀적으로 반복하면 된다는 아이디어가 떠올랐다.
그래서 아래와 같이 개선된 코드를 작성할 수 있었다.
2️⃣ Console로 input을 받는 메서드 테스트 코드 작성하기
미션에서는 console로 input을 받는다. 이때 들어오는 input에 대해 예외처리하는 것은 중요하다.
여러 경우의 잘못된 입력값을 잘 처리해주고 있는지 테스트가 필요하다.
❓그러면 테스트할 때 console로 입력받는 부분은 어떻게 처리할 수 있을까
❗작성 방법
1. 테스트용 mock InputStream을 만든다.
2. 이때 생성한 InputStream을 System.in에 넣어준다.
- input을 InputStream으로 만드는 메서드 생성
public static InputStream generateUserInput(String input) {
return new ByteArrayInputStream(input.getBytes());
}
- System.setIn에 input 값을 세팅해준다.
InputStream in = generateUserInput(string);
System.setIn(in);
- 최종 코드
🧐 구현하면서 고민했던 것들
1️⃣ enum 활용
연관성이 있는 상수는 static final 대신 enum을 활용한다.
- 우아한 테크코스 3주차 피드백 중
다음은 게임의 성공, 실패 결과를 출력하는 메서드이다.
이때, 비슷한 성격의 값이 하드 코딩되어 있다. 공통 피드백을 반영하여 enum 타입으로 변경했다.
❓이때 고민되었던 것은 연관성 있는 상수를 묶는다는게 성공이라는 상수에 (1, 5000, false) 와 같이 여러가지 연관된 값이 있을 때 만들어주는 것인지 카테고리 자체가 연관성이 있을 때 enum으로 빼는 건지 고민이 되었다.
피드백에 있던 예시의 일부이다.
FIRST(6, 2_000_000_000),
SECOND(5, 30_000_000),
THIRD(5, 1_500_000),
FOURTH(4, 50_000),
FIFTH(3, 5_000),
MISS(0, 0);
지난 미션에 로또에서는 1등은 6개 숫자가 일치하고 상금이 20억이다. 이렇게 연관된 값이 있을 경우 enum으로 만들어줬다.
그래서 지금 같은 성공, 실패라는 문자 하나만 출력하는 enum을 사용하는 것이 옳을지 static final로 만드는 것을 옳을지 고민되었다.
/* 소프트웨어에는 정답은 없다. 하지만 더 나은 방향은 뭘까? ㅠㅠㅠ */
그래서 enum의 장점을 다시 살펴봤다.
👉 우아한 형제들 기술 블로그, Java Enum 활용기
읽으면서 생각했다. 현재 성공, 실패를 true, false로 메서드에서 입력을 받고 그것을 문자로 치환해준다.
그렇다면 성공 실패라는 것이 여러 형태로 구현될 수 있다는 것이다. 나중에 다른 클래스에서 1, 0으로 판단할 수도 있다.
그래! 결정했어! enum으로 분리하자.
이때 true, false는 현재 사용하지 않지만 이런 식으로 나중에 확장이 가능하다는걸 나타내기 위해서 작성해두었다.
public enum GameResult {
WIN(true, "성공"),
OVER(false, "실패");
private final boolean isSuccess;
private final String message;
GameResult(boolean isSuccess, String message) {
this.isSuccess = isSuccess;
this.message = message;
}
public String getMessage() {
return message;
}
}
다음과 같이 추가 타입이 필요한 경우에도 Enum 상수와 get메소드만 추가하면 된다!
2️⃣ InputView의 메서드 분리
메서드 길이가 길어진다면 한 메서드에서 여러 일을 하려고 하는 경우일 가능성이 높다고 한다.
따라서 아래 코드를 분리하도록 노력해보자!
현재 InputView에서 Console로 입력받는 로직 외에 문구 출력, Validation, try-catch로 다시 입력을 받는 로직, 필요한 데이터 형식으로 바꾸는 로직까지 포함되어 있어 한 메서드의 덩치가 꽤 크다고 생각했다.
그래서 InputView에서는 입력 문구와 입력, 그리고 데이터에 대한 유효성 검사까지만 책임을 주었다.
그리고 필요한 형태로 가공하는 것은 Controller로 책임을 넘겨 주었다.
그 외에도 메서드 분리를 위해 많은 코드를 수정했다.
3️⃣ 책임왕 Controller를 살려줘 (っ °Д °;)っ
현재 나는 Controller를 View와 Domain을 연결해주며 게임의 흐름을 관리하는 역할을 부여했다.
이때 한 메서드마다 코드가 너무 길어서 여러 역할을 하고 있는 듯 보인다. 리팩터링 해보자!
- gameStart() | 게임의 흐름을 담당하는 메서드
- gameSetUp() | 게임 세팅하는 메서드
- 다리 건너기의 다리는 게임이 시작되면 자동으로 세팅되어 있으며 한 번 시작한 게임에서 변경되지 않고 유지된다.
- oneGame() | 게임 1판
2. 최종 폴더 구조 🗂️
├─main
│ └─java
│ └─bridge
│ │ Application.java
│ │ BridgeGame.java
│ │ BridgeMaker.java
│ │ BridgeNumberGenerator.java
│ │ BridgeRandomNumberGenerator.java
│ │
│ ├─controller
│ │ BridgeGameController.java
│ │
│ ├─domain
│ │ GameResult.java
│ │
│ ├─validator
│ │ BlockValidator.java
│ │ NumberValidator.java
│ │
│ └─view
│ InputView.java
│ OutputView.java
│
└─test
└─java
└─bridge
│ ApplicationTest.java
│ BridgeGameTest.java
│ BridgeMakerTest.java
│
└─view
InputViewTest.java
3. 구현 완료
이번 주에 느낀 점은 '항상 이유를 생각해보자'이다.
예를 들어 enum으로 static final로 구현한 상수를 분리할 때
왜 enum으로 구현하는 것이 더 나은 방향인지 그리고 enum으로 구현했을 때 어떤 점이 개선될지를 고민했다.
그래서 앞으로 개발을 할 때 항상 기술 스택이나 어떤 구조를 선택할 때 이유를 끊임없이 생각해야겠다고 느꼈다.
'대외활동 > [미션] 우아한테크코스 프리코스 | Java' 카테고리의 다른 글
[우아한테크코스] 숫자 야구(baseball) 리팩토링 (0) | 2022.11.28 |
---|---|
[우아한테크코스] 객체를 객체스럽게 사용하도록 리팩토링해라. (0) | 2022.11.26 |
[우아한테크코스] 프리코스 3주차 : 로또 (lotto) (0) | 2022.11.13 |
[우아한테크코스] 프리코스 2주차 : 숫자 야구(baseball) (0) | 2022.11.07 |
댓글