TDD를 공부하면서 TDD를 활용한 볼링게임을 구현해보기로 하였다.
아래의 할 일 목록 은 구현에 앞서 생각해본 볼링게임을 객체지향적으로 나태 내기 위한 방법이다.
이 방법이 정답은 아니지만 할 일 목록을 생각하면서 내가 어떤 구현을 해나갈지 명확해졌다.
다만 아직은 할 일을 작은 단위로 나누지 못한 것 같아서 우선 할 수 있는 것부터 하기로 했다.
할 일 목록
[ ] 볼링 객체를 만든다.
[ ] 볼링의 하위 객체로는 볼링 플레이어와 볼링 점수 계산기가 존재한다. (플레이어와 점수 계산기는 1 : 1 관계를 갖는다.)
[ ] 점수 계산기를 통해 계산된 점수는 점수 표출기를 통해 점수를 볼 수 있어야 한다.
[ ] 볼링의 프레임과 핀은 점수 계산기와 연관이 있는데 이것은 나중에 생각해본다.
[ ] 실패하는 테스트를 만들고 스텁으로 우선 빠르게 테스트를 작성할 것이다.
[v] 실패하는 테스트를 만들고 스텁으로 우선 빠르게 테스트를 작성할 것이다.
[v] 볼링 객체를 만든다.
우선 가장 쉽지만, TDD의 시작점인 할 일을 처리하기로 했다. 한 번에 하나씩 할 일을 처리하는 것도 중요하지만
이 두 개의 할 일은 한 번에 처리할 수 있다고 생각이 들어 볼링 객체를 스텁으로 만들 것이다.
public class BowlingTest {
@Test
void 볼링객체가_존재하는지_확인한다(){
Bowling bowling = new Bowling();
}
}
당연하지만 볼링 객체가 현재는 존재하지 않기 때문에 실패하였고 빠르게 초록 막대를 보기 위하여 스텁 구현을 하였다.
//자바는 내부적으로 생성자가 존재하지 않는다면 디폴트 생성자를 제공한다.
public class Bowling {
}
테스트가 성공한다. 그럼 스텁구현을 수습할 차례이다. 처리에 앞서 볼링을 구현하기 위해서는
볼링의 하위 객체로는 볼링 플레이어와 볼링 점수 계산기가 존재한다. (플레이어와 점수 계산기는 1 : 1 관계를 갖는다.) 을
해결해야 하는데 당장은 어떻게 구현할지 모르겠다. 그래서 과감하게 플레이어와 점수 계산기도 스텁으로 우선 처리를 하기로 했다.
@Test
void 볼링게이머와_볼링점수가_존재하는지_확인한다(){
BowlingGamer gamer = new BowlingGamer();
BowlingScore bowlingScore = new BowlingScore();
}
TDD를 하다 보니 할 일 목록의 단위가 너무 커서 조금 더 작은 단위로 나눠야겠다는 생각이 들었다.
생각해보니 볼링은 프레임, 투구, 핀 이라는 개념이 있다. 이 개념들을 어떻게 처리할지 생각해서 할 일 목록에 추가하였다.
단 할 일을 추가하기 전에 리팩토링부터!
[ ] 볼링 객체를 만든다.
[] 볼링 객체는 10개의 핀을 가지고 있고 21번의 투구 기회가 있다.
할 일을 추가하였고, 이것을 처리하기 위해 볼링 객체에 핀과 투구 갯수를 넣었다. (단 테스트부터)
// 객체를 확인하는 테스트가 여러개일 필요가 없다고 생각하여 합쳤다.
@Test
void 객체가_존재하는지_확인한다(){
Bowling bowling = new Bowling();
BowlingPlayer player = new BowlingPlayer();
BowlingScore score = new BowlingScore();
}
@Test
void 볼링객체의_값을_검증(){
Bowling bowling = new Bowling();
assertEquals(bowling.getPin(), 10);
assertEquals(bowling.getPitchingCount(), 21);
}
public class Bowling {
private int pin;
private int pitchingCount;
public Bowling(){
this.pin = 10;
this.pitchingCount = 21;
}
public int getPin(){
return pin;
}
public int getPitchingCount(){
return pitchingCount;
}
}
작성하고 나니 문제가 있다. 볼링게임은 프레임이 끝나는 시점에 핀의 개수가 초기화되는데
프레임을 어떻게 구현해야 할지 명확히 생각이 안 난다. 그렇다고 클래스로 따로 빼주기에는 부담이 간다.
그래서 메소드를 추가하여 핀의 개수를 10개로 초기화할 생각인데 이때 나중에 생길 문제점이 있을까? 를 고민하였지만
현시점에서는 문제 될 게 없다고 판단된다. 그럼 할 일 목록을 추가 하자.
[] 볼링의 프레임은 총 10 프레임이고 한 프레임이 끝나면 핀의 개수는 10개로 초기화된다.
[] 투구 2번 끝나면 한 프레임도 끝낸다. 단 마지막 프레임은 3번의 투구가 필요하다.
[] 투구를 통해 몇 개의 핀을 쓰러트렸는지 알아야 한다.
public class BowlingTest {
@Test
void 객체가_존재하는지_확인한다(){
Bowling bowling = new Bowling();
BowlingPlayer player = new BowlingPlayer();
BowlingScore score = new BowlingScore();
}
@Test
void 볼링객체의_값을_검증(){
Bowling bowling = new Bowling();
assertEquals(bowling.getPin(), 10);
assertEquals(bowling.getPitchingCount(), 21);
assertTrue(bowling.getFrame() == 10);
}
@Test
void 투구와_프레임_검증(){
Bowling bowling = new Bowling();
assertTrue(bowling.getPin() == 10); // 투구전 핀은 10개
assertTrue(bowling.getFrame() == 10); // 프레임은 10으로 시작
bowling.pitch(); // 첫 번째 투구
System.out.println(bowling.getPin()); // 투구후 핀은 모른다.
assertTrue(bowling.getFrame() == 10); // 첫 투구가 끝나도 프레임은 10
bowling.pitch(); // 두 번째 투구
assertTrue(bowling.getFrame() == 9); // 두 번째 투구가 끝나면 프레임은 9
assertTrue(bowling.getPin() == 10); // 새로운 프레임이 시작되면 핀은 초기화
}
}
public class Bowling {
private int pin;
private int pitchingCount;
private int frame;
public Bowling(){
this.pin = 10;
this.pitchingCount = 21;
this.frame = frameCalc();
}
public int getPin(){
return pin;
}
public int getPitchingCount(){
return pitchingCount;
}
public int getFrame(){
return frame;
}
public void pitch(){
int nowFrame = frameCalc();
this.pin -= (int)((Math.random()*10000)%10);
this.pitchingCount -= 1;
this.frame = pitchingCount / 2;
if(nowFrame != frameCalc()){
this.pin = 10;
}
}
private int frameCalc(){
return this.pitchingCount / 2;
}
}
여기까지 TDD를 진행하면서 느낀 문제가 있는데
- 1. 자꾸 테스트보다 기능 추가를 먼저 생각한다.
- 2. 공을 굴리는 pitch 메소드가 볼링 객체에 있는 게 맞지 않는 것 같다.
1번의 경우. 다행히도 기능 추가를 하기 전에 다시 테스트로 돌아가고는 있지만 역시 습관은 무섭다.
2번의 경우. 아마 내 생각엔 이 메소드는 Bowling Player 객체로 이동해야 할 것 같다.
(중간에 BowlingGamer에서 Bowling Player로 수정하였다.)
그러면 단일 책임 원칙(SRP)을 지킬 수 있을 것 같다.
[] 투구(pitch)는 Bowling Player 객체가 가지고 있어야 한다.
[] 투구(pitch) 후 쓰러진 pin은 BowlingScore에 전달되어야 한다.
다음장은 지금까지 느낀 문제점을 해결하고 조금 더 완성도 높은 볼링게임을 위해 리팩토링 할 예정이며,
점수 관련 내용도 추가해 볼 것이다.
'공부 > TDD' 카테고리의 다른 글
볼링게임 TDD [3] (0) | 2020.10.03 |
---|---|
볼링게임 TDD [2] (0) | 2020.10.02 |
[OKKYCON: 2018] 이규원 - 당신들의 TDD가 실패하는 이유 (0) | 2020.09.26 |
[OKKYCON: 2018] 자바지기(박재성)- TDD 의식적으로 연습하기 (0) | 2020.09.20 |
테스트 주도 개발 [10 - 17장] (0) | 2020.09.12 |
댓글