갈피를 못잡아서 피드백 강의부터 보았다.
🚀 강의의 내용은 이러했다
- 단위 테스트를 먼저 작성한 후, production 코드작성
- 실패하는 단위테스트를 꼭 봐야함- ToDoList 작성하여 단위테스트
- 작은 단위로 쪼개어 단위테스트
- 리팩토링
- 클래스 잘 나누기
"정답이 있다", "마스터하였다" 등의 개념이 아니라 끊임 없이 고민하는 것.
따라치려고 하는 것 보다는, 감 잡고 또 해보고.. 영상 또 보고.. 또 해보고..
이런 식으로 하려고 한다.
🏀 To Do List
- 1 ~ 9까지의 서로 다른 수 3자리 생성(컴퓨터용)
- 1 ~ 9까지의 서로 다른 수 3자리 생성(사용자용)
- 볼 맞히기
- 사용자 볼 하나 당...
- ...
- 게임 종료하기
- ...
이런 식으로 큼지막한 ToDoList가 나왔다.
감은 잡았지만, 막상 하려니 막막했다.
🏀 지우고 지우기를 반복했다.
- 아까 봤던 건 이게 아닌데..
- 메서드를 빼야할 일이 생겨( indent depth / 한 메서드는 한 일만 )
빼다보니, 네이밍이 적절하지 않게 되는 문제가 많았다.- 그냥 무언가 이상해...
- ...
이 과정을 반복하다가 '가장 작은 단위'로 쪼개야 편해진다는 걸 알았다.
(잘하고 있다고는 장담할 수 없지만..)
🏀 사용자의 입력은
- 1~9 사이
- 3 자리
- 서로 다른 수
- - - -
: 아쉬운 점
🏀 1~9 사이
@Test
@DisplayName("사용자가 1~9사이의 숫자를 입력하였는지 체크")
void test(){
BallMaker ballMaker = new BallMaker();
assertThat(ballMaker.checkRange(0)).isFalse();
assertThat(ballMaker.checkRange(10)).isFalse();
assertThat(ballMaker.checkRange(1)).isTrue();
assertThat(ballMaker.checkRange(9)).isTrue();
}
ParameterizedTest 를 이용하지 않음(테스트코드 리팩토링)
언밀하게는 한 숫자의 범위를 체크하는 것이니 이름이 조금..
DisPlayName을 이런식으로 사용하는 게 맞나..
public class BallMaker {
private static final int MIN_NUM=1;
private static final int MAX_NUM=9;
private static final int DIGIT_NUM=3;
//범위와 자릿수는 얼마든지 바뀔 수 있으니 빼두었다.
public boolean checkRange(int i) {
//return i>=1 && i<=9;
return i>=MIN_NUM && i<=MAX_NUM;
}
}
1부터(MIN_NUM) 9까지(MAX_NUM) 3자리(DIGIT_NUM)는 어찌보면 게임룰이니
BallMaker가 아니라 더 외부에(게임 자체와 관련된) 있어야 하는 것일까?
생성자를 통해서 field를 초기화 해야할까?
🏀 3 자리수
@Test
@DisplayName("사용자가 3자리수를 입력하였는지")
void test2(){
BallMaker ballMaker = new BallMaker();
assertThat(ballMaker.checkNum(11)).isFalse();
assertThat(ballMaker.checkNum(1111)).isFalse();
assertThat(ballMaker.checkNum(111)).isTrue();
}
BallMaker ballMaker = new BallMaker()는 공통이었구나.. @BeforeEach
어우.. ParameterizedTest 빨리 적용하던가 해야겠다
public boolean checkNum(int i) {
//return i/100 != 0 && i/1000 == 0;
return MyMathUtil.checkDigit(i,DIGIT_NUM);
}
@Test
@DisplayName("n자리 이하 수라면 10^n로 나누면 몫이 0이야")
void test3(){
assertThat(MyMathUtil.checkDigitUpper(0,3)).isTrue();
assertThat(MyMathUtil.checkDigitUpper(1,3)).isTrue();
assertThat(MyMathUtil.checkDigitUpper(111,3)).isTrue();
assertThat(MyMathUtil.checkDigitUpper(1111,3)).isFalse();
}
@DisPlayName을 이런식으로 사용해도 되나..? - 2
3자리 이하면 true, 4자리 부터는 false인데 어떻게 value를 짜야 적당한지 모르겠다.
@Test
@DisplayName("n자리 이상 수라면 10^(n-1)로 나누면 몫이 0이 아니야")
void test4(){
assertThat(MyMathUtil.checkDigitLower(11,3)).isFalse();
assertThat(MyMathUtil.checkDigitLower(111,3)).isTrue();
assertThat(MyMathUtil.checkDigitLower(1111,3)).isTrue();
}
이렇게 3개부터 true라면 values(2,3,4) 하면 될까..?
public class MyMathUtil {
public static boolean checkDigitUpper(int i, int digit) {
return i/(int)Math.pow(10,digit) == 0;
}
public static boolean checkDigitLower(int i, int digit) {
return i/(int)Math.pow(10,digit-1) != 0;
}
}
이 메서드들은 먼 훗날 private이어야 함
private method를 reflection이용하여 테스트 하는 것까지는
강사님께서도 좀 그렇다는 뉘앙스
그럼 private로 바꾸고 나서는 테스트코드에 에러가 날텐데
테스트 통과 후 private로 바꾸면 그 테스트코드는 지우나 그럼..?
@Test
@DisplayName("위 두개를 합침")
void test5(){
assertThat(MyMathUtil.checkDigit(11,3)).isFalse();
assertThat(MyMathUtil.checkDigit(1111,3)).isFalse();
assertThat(MyMathUtil.checkDigit(111,3)).isTrue();
}
@Test
@DisplayName("사용자가 3자리 수 입력하였는지, MyMathUtil적용 후 다시 확인")
void test6(){
BallMaker ballMaker = new BallMaker();
assertThat(ballMaker.checkNum(11)).isFalse();
assertThat(ballMaker.checkNum(1111)).isFalse();
assertThat(ballMaker.checkNum(111)).isTrue();
}
사실 이렇게 하면 안된다(test2와 똑같잖아..그러지마)
public static boolean checkDigit(int i, int digit) {
//return checkDigitLower(i,digit)&&checkDigitUpper(i,digit);
//두개로 나눌 필요가 없었구나..
//refactoring - 1
//return i/(int)Math.pow(10,digit) == 0 && i/(int)Math.pow(10,digit-1) != 0;
//refactoring - 2
int n = (int)Math.pow(10,digit);
return i/n == 0 && i/(n/10) != 0;
}
숫자 생성 시의 3가지 조건중 겨우 2가지 밖에 안되는데
이렇게 심각하게 고민하는 시간을 가졌다.
아쉬웠던 점들을 고려하여, 내가 짠 것을 "싹 다 지우고"
다시.. 다시.. 시작할 것이다.
어쩌면 잘못된 감을 잡은건데 괜히 열심히 하는 것은 아닌가 싶다.
내가 믿을 것은 오직 팀원들과의 피드백뿐이다.
다음 포스팅은 오늘의 아쉬운 점이 개선 된 더 나은 코드이다.
※ 리팩토링은 보이는 즉시 해야한다. 미뤄서 고생했다.
※ commit을 안하여서 이 모든 과정을 기억에 의존하여 작성하다 보니 그 당시 만큼의 생동감을 표현하지 못했다.