Skip to content

rkdalsdn94/typescript-bowling-game

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

14 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

์—ฌ๊ธฐ ๋งํฌ์— ์žˆ๋Š” ๋ฐฑ๋ช…์„๋‹˜์˜ README๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•ด์™”๋‹ค.
๊ตฌํ˜„ ๋ฐฉ๋ฒ•๋„ ๊ฐ™๋‹ค. ๋Œ€์‹  TypeScript๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.


0. ๊ฐœ์š”

์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋ฐฅ์•„์ €์”จ๋ฅผ ๋น„๋กฏํ•œ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด 10์—ฌ๊ฐ„ TDD๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•œ ์˜ˆ์ œ๋กœ ์‚ฌ์šฉํ•œ Bowling Game์„ TDD๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ์„ค๋ช…ํ•˜๊ณ , TDD์— ๋Œ€ํ•œ ๋ฐ˜๋ฐœ๊ณผ ์ด์— ๋Œ€ํ•œ ๋‹ต๋ณ€์— ๋Œ€ํ•ด ์•Œ์•„๋ณธ๋‹ค.

Bowling Game Demo

  • ๊ทœ์น™
    • ๋ณผ๋ง ๊ฒŒ์ž„์€ 10๊ฐœ์˜ ํ”„๋ ˆ์ž„์œผ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.
    • ๊ฐ ํ”„๋ ˆ์ž„์€ ๋Œ€๊ฐœ 2 ๋กค์„ ๊ฐ–๋Š”๋‹ค(10๊ฐœ์˜ ํ•€์„ ์“ฐ๋Ÿฌ ๋œจ๋ฆฌ๊ธฐ ์œ„ํ•ด 2๋ฒˆ์˜ ๊ธฐํšŒ๋ฅผ ๊ฐ–๋Š”๋‹ค).
    • Spare: 10 + next first roll์—์„œ ์“ฐ๋Ÿฌ ๋œจ๋ฆฐ ํ•€์ˆ˜.
    • Strike: 10 + next two rolls์—์„œ ์“ฐ๋Ÿฌ ๋œจ๋ฆฐ ํ•€์ˆ˜.
    • 10th ํ”„๋ ˆ์ž„์€ ํŠน๋ณ„. spare ์ฒ˜๋ฆฌํ•˜๋ฉด 3๋ฒˆ ๋˜์งˆ ์ˆ˜ ์žˆ์Œ.

์ด ์˜ˆ์ œ์˜ ๋ชฉ์ 

Game์ด๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ด ์˜ˆ์ œ์˜ ๋ชฉ์ ์ด๋‹ค.

  • Game ํด๋ž˜์Šค๋Š”
    • roll๊ณผ score๋ผ๋Š” 2๊ฐœ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ–๋Š”๋‹ค.
    • roll ๋ฉ”์†Œ๋“œ๋Š” ball์„ rollํ•  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ๋œ๋‹ค. ์ธ์ž๋กœ๋Š” ์“ฐ๋Ÿฌ๋œจ๋ฆฐ ํ•€์ˆ˜๋ฅผ ๊ฐ–๋Š”๋‹ค.
    • score ๋ฉ”์†Œ๋“œ๋Š” ๊ฒŒ์ž„์ด ๋๋‚œ ํ›„์—๋งŒ ํ˜ธ์ถœ๋˜์–ด ๊ฒŒ์ž„์˜ ์ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ฐ„๋‹จํ•œ ์„ค๊ณ„ ๊ณผ์ •

  • ๊ฒŒ์ž„์€ 10๊ฐœ์˜ ํ”„๋ ˆ์ž„์„ ๊ฐ–๋Š”๋‹ค.
    • Game์€ roll, score ํ•จ์ˆ˜๋ฅผ ๊ฐ–๋Š”๋‹ค.
    • Game์€ 10๊ฐœ์˜ Frame์„ ๊ฐ–๋Š”๋‹ค.

  • Frame์€ 1..2๊ฐœ์˜ Roll์„ ๊ฐ–๋Š”๋‹ค.
    • 10๋ฒˆ ํ”„๋ ˆ์ž„์€ ์˜ˆ์™ธ๋ฅผ ๊ฐ–๋Š”๋‹ค(1..2 roll์„ ๊ฐ–๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ 2..3 roll์„ ๊ฐ–๋Š”๋‹ค).
    • ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ ์ด๋Ÿฐ ์˜ˆ์™ธ๋ฅผ ์–ด๋–ป๊ฒŒ ํ‘œํ˜„ํ•˜๋‚˜ ???

** score ํ•จ์ˆ˜์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜

frame์ˆ˜๋งŒํผ loop๋ฅผ ๋Œ๋ฉด์„œ ๊ฐ frame์˜ ์ ์ˆ˜๋ฅผ ํ•ฉ์‚ฐํ•  ๊ฒƒ์ด๋‹ค.

frame์€ roll์ˆ˜ ๋งŒํผ loop๋ฅผ ๋Œ๋ฉด์„œ ์ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•  ๊ฒƒ์ด๋‹ค. strike, spare๋ฅผ ์œ„ํ•ด์„œ look ahead roll์„ ํ•ด์•ผ ํ•œ๋‹ค.

์ž ์ด์ œ ์„ค๊ณ„๊ฐ€ ์žˆ๋‹ค. ์ด์ œ TDD๋ฅผ ํ•  ์ฐจ๋ก€

TDD์—์„œ ์ด์ƒํ•œ ์ผ์„ ํ•œ๋‹ค. ์„ค๊ณ„๋ฅผ ๋ฌด์‹œํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์™„์ „ํžˆ ๋ฌด์‹œํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ  ๋”ฐ๋ฅด์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค. ๋‹จ์ง€ ๊ฐ€์ด๋“œ ๋ผ์ธ์˜ ์ผ์ •์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

public class BowlingTest {
  @Test
  public void nothing() {
  }
}
  • ์•„๋ฌด๊ฒƒ๋„ ์—†๋Š” nothing์ด๋ผ๋Š” ํ…Œ์ŠคํŠธ๋กœ ์‹œ์ž‘. ์ด๊ฒƒ๋„ ์‹คํ–‰ํ•ด ๋ณธ๋‹ค. ๋ฐฅ ์•„์ €์”จ๋Š” ํ•ญ์ƒ ๋ญ”๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์‹ฌ์ง€์–ด ์˜๋ฏธ ์žˆ๋Š” ์ฝ”๋“œ๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋”๋ผ๋„ ์‹คํ–‰๋˜๋Š” ๋ญ”๊ฐ€๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณค ์ง€์šด๋‹ค. ์•„๋งˆ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์„ ์œ„ํ•œ ์„ค์ •์ด ์ œ๋Œ€๋กœ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š”๊ฐ€๋ณด๋‹ค. ๋‚˜์ค‘์— ์•Œ์•˜์ง€๋งŒ ํ•ญ์ƒ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋กœ ์ž‘์—…ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค. ์ด๊ฒŒ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ๋Š” ํŽธ์•ˆํ•จ์„ ์ค€๋‹ค.
  • ๋ฌด์Šจ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋‚˜ ?
    • failing unit test๊ฐ€ ์žˆ๊ธฐ ์ „์—๋Š” production code๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
    • ํ•˜์ง€๋งŒ ์ด๋ฏธ ๊ฐœ๋ฐœ์ž๋Š” ์–ด๋–ค production code๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š”์ง€ ์•ˆ๋‹ค. public class Game์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ...
    • ๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” public class Game์„ ์ž‘์„ฑํ•˜๋„๋ก ํ—ˆ๊ฐ€ ๋ฐ›์ง€ ์•Š์•˜๋‹ค. ์šฐ๋ฆฌ๋Š” ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.
  • ์–ด๋–ค ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋ ๊นŒ ?
    • ์–ด๋–ค failing test๋ฅผ ์ž‘์„ฑํ•ด์•ผ Game.java์— public class Game์„ ์„ ์–ธํ•˜๊ฒŒ ๋ ๊นŒ ?
  • ์ด์ œ๋ถ€ํ„ฐ red-green-refactor cycle๋กœ ๋“ค์–ด๊ฐ€์ž.
    • red phase: next most interesting case but still really simple
    • green phase: make it pass
    • blue phase: refactor
  • ๊ฐ€์žฅ ์‰ฝ๊ณ (๊ฐ„๋‹จํ•˜๊ณ ) ํฅ๋ฏธ๋กœ์šด(easy/simple and interesting) ํ…Œ์ŠคํŠธ๋ถ€ํ„ฐ ์ž‘์„ฑ

1. canCreateGame

1.1 add failing test

@Test
public void canCreateGame() {
  Game g = new Game();
}

1.2 make it pass

  • IDE์˜ hot fix๋ฅผ ์ด์šฉ(F2: next error, opt+enter: hot fix)

2. canRoll

2.1 add failing test

  • ์Šค์ฝ”์–ด๋ฅผ ๋ฐ”๋กœ ๊ณ„์‚ฐํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ์ด๋ฅผ ์œ„ํ•œ ๊ณผ์ •์œผ๋กœ canRoll์„ ๋จผ์ € ์ถ”๊ฐ€
  • ๋„˜์–ด์ง„ pin์ˆ˜๊ฐ€ 0์ธ ๊ฒƒ์— ๋Œ€ํ•œ failing test๋ฅผ ๋จผ์ € ์ถ”๊ฐ€
@Test
public void canRoll() {
    Game g = new Game();
    g.roll(0);
}

2.2 make it pass

  • make it pass by using hot fix

2.3 refactor

  • ํ…Œ์ŠคํŠธ์— ์žˆ๋Š” ์ค‘๋ณต(Game game = new Game();) ์ œ๊ฑฐ
  • ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ extract field(initialize๋Š” setUp์„ ์„ ํƒ)

2.3.1 canRoll

  • ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ ์ œ๊ฑฐ(@Test public void canCreate() ๋ฉ”์†Œ๋“œ๋Š” ๋ถˆํ•„์š”)

3. gutterGame

3.1 add failing test

  • score๋ฅผ ๋ฐ”๋กœ ํ˜ธ์ถœํ•˜๊ณ  ์‹ถ์ง€๋งŒ, ๊ฒŒ์ž„์ด ๋๋‚˜์•ผ๋งŒ scoreํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฒŒ์ž„์„ ๋๋‚ด๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ gutter game์ด๋‹ค.
@Test
public void gutterGame() {
    for(int i = 0; i < 20; i++)
        game.roll(0);
    assertThat(game.score(), is(0));
}

3.2 make it pass

4. allOnes

4.1 add failing test

  • next most simple and interesting test case
@Test
public void allOnes() {
    for(int i = 0; i < 20; i++)
        game.roll(1);
    assertThat(game.score(), is(20));
}

4.2 make it pass

private Integer score = 0;

public void roll(int pins) {
    score += pins;
}

public Integer score() {
    return score;
}

4.3 refactor

  • extract variable
    • gutterGame()์—์„œ rolls, pins๋ฅผ ์ถ”์ถœ
@Test
public void gutterGame() {
    int rolls = 20;
    int pins = 0;
    for(int i = 0; i < rolls; i++) {
        game.roll(pins);
    }
    assertThat(game.score(), is(0));
}
  • extract method - rollMany()
@Test
public void gutterGame() {
    int rolls = 20;
    int pins = 0;
    rollMany(rolls, pins);
    assertThat(game.score(), is(0));
}

private void rollMany(int rolls, int pins) {
    for(int i = 0; i < rolls; i++)
        game.roll(pins);
}
  • inline variables
@Test
public void gutterGame() {
    rollMany(20, 0);
    assertThat(game.score(), is(0));
}

5. oneSpare

5.1 add failing test

  • gutter, allOne์ด ์žˆ์œผ๋‹ˆ allTwo๋ฅผ ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ์œผ๋‚˜ ์ด๊ฑด ์ž˜ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค.
  • ๋ป”ํžˆ ๋™์ž‘ํ•  ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋Š” ํ…Œ์ŠคํŠธ๋Š” ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • allThree, allFour๋„ ์ž˜ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ allFive๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค. spare๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—.
  • spare์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ฐจ๋ก€์ด๋‹ค. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ spare๋Š” ์–ด๋–ค ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„๊นŒ ? one spare + gutter.
@Test
public void oneSpare() {
    game.roll(5);
    game.roll(5); // spare
    game.roll(3);
    rollMany(17, 0);
    assertThat(game.score(), is(16));
}

5.2 make it pass

  • ๊ทผ๋ฐ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค.
public void roll(int pins) {
    if(pins + lastPins == 10)
    ...
}
  • ์œ„์™€ ๊ฐ™์ด ํ•˜๋ ค๋‹ค ๋ณด๋‹ˆ ์ด์ƒํ•˜๋‹ค. ํ”Œ๋ž˜๊ทธ ๋ณ€์ˆ˜, ์ •์  ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ณ โ€ฆ
  • ์ด์ฒ˜๋Ÿผ ๋”์ฐํ•œ ์ผ์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ธธ๋•Œ๋งˆ ์ž ์‹œ ๋ฌผ๋Ÿฌ๋‚˜์•ผ ํ•œ๋‹ค.
  • ๋ญ”๊ฐ€ ๋””์ž์ธ์ด ์ž˜ ๋ชป๋œ ๊ฒƒ์ด๋‹ค.
  • ๋””์ž์ธ ์›์น™์ด ์œ„๋ฐฐ๋œ ๊ฒƒ์ด ์žˆ๋‹ค.
  • ์ฒซ๋ฒˆ์งธ ์›์น™: ์Šค์ฝ”์–ด๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š” ์ด๋ฆ„์„ ๊ฐ–๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€ ?
  • score ํ•จ์ˆ˜์ด๋‹ค. ๊ทผ๋ฐ ์‹ค์ œ๋กœ score๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜๋Š” roll ํ•จ์ˆ˜์ด๋‹ค.
  • ์ž˜๋ชป๋œ ์ฑ…์ž„ ํ• ๋‹น(misplaced responsibility)์ด ๋””์ž์ธ ์›์น™, ์ž˜๋ชป๋œ ๋””์ž์ธ ๋ƒ„์ƒˆ์ด๋‹ค.
  • roll์—์„  ๊ฐ roll์„ ์ €์žฅํ•˜๊ณ , score์—์„œ ๊ณ„์‚ฐ์„ ํ•ด์•ผ ํ•œ๋‹ค.
  • ์–ด์ฉŒ์ง€. refactoring. ๊ทผ๋ฐ failing test๊ฐ€ ์žˆ๋‹ค. @Ignore ์ฒ˜๋ฆฌโ€ฆ

5.3 refactoring

  • ์ด์ œ ํ…Œ์ŠคํŠธ๊ฐ€ ์ˆ˜ํ–‰๋˜๋‹ˆ ๋ฆฌํŒฉํ† ๋งํ•˜์ž.
  • Roll์„ ๋ฐฐ์—ด์— ์ €์žฅํ•˜์ž.
  • ์ด์— ์ž˜๋ชป๋œ ์ฑ…์ž„ ํ• ๋‹น์ด ํ•ด์†Œ๋˜์—ˆ๋‹ค.
public class Game {
    private int[] rolls = new int[21];
    private int currentRoll = 0;

    public void roll(int pins) {
        rolls[currentRoll++] = pins;
    }

    public Integer score() {
        int score = 0;
        for(int i = 0; i < rolls.length; i++)
            score += rolls[i];
        return score;
    }
}
  • frame์„ ๋„์ž…ํ•˜์—ฌ ์ฝ๊ธฐ ์‰ฝ๊ฒŒํ•œ๋‹ค.
public Integer score() {
    int score = 0;
    int i = 0;
    for(int frame = 0; frame < 10; frame++) {
        score += rolls[i] + rolls[i + 1];
        i += 2;
    }
    return score;
}

7. oneSpare

7.1 add failing test

  • @Ignore ์ œ๊ฑฐ

7.2 make it pass

if(rolls[i] + rolls[i + 1] == 10) { // spare
    score += 10 + rolls[i + 2];
    i += 2;
}
else {
    score += rolls[i] + rolls[i + 1];
    i += 2;
}

7.3 refactoring

  • rename i to firstFrame
  • ์ฒ˜์Œ๋ถ€ํ„ฐ firstFrame์ด๋ผ๋Š” ๋ณ€์ˆ˜๋ช…์„ ์ฐพ์€ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ์ฐจ๋ก€์˜ ์‹œ๋„๋กœ ์ฐพ์Œ

7.3.1 refactoring

  • extract method isSpare

7.3.1 oneSpare - refactoring

  • remove duplication - comment // spare

7.3.1 oneSpare - refactoring

  • extract method for readability(rollSpare)
@Test
public void oneSpare() {
    rollSpare();
    game.roll(3);
    rollMany(17, 0);
    assertThat(game.score(), is(16));
}

private void rollSpare() {
    game.roll(5);
    game.roll(5);
}

8. oneStrike

8.1 add failing test

@Test
public void oneStrike() {
    game.roll(10);
    game.roll(5);
    game.roll(3);
    rollMany(16, 0);
    assertThat(game.score(), is(26));
}

8.1 make it pass

if(rolls[firstFrame] == 10) { // strike
    score += 10 + rolls[firstFrame + 1] + rolls[firstFrame + 2];
    firstFrame += 1;
}
else if(isSpare(firstFrame)) {
...

8.3 refactoring

  • extract method isStrike

8.3.1 refactoring

  • extract method for readabiliy(nextTwoBallsForStrike, nextBallForSpare, nextBallsInFrame)
if(isStrike(firstFrame)) {
    score += 10 + nextTwoBallsForStrike(firstFrame);
    firstFrame += 1;
}
else if(isSpare(firstFrame)) {
    score += 10 + nextBallForSpare(firstFrame);
    firstFrame += 2;
}
else {
    score += nextBallsInFrame(firstFrame);
    firstFrame += 2;
}

8.3.2 refactoring

  • extract method for readabiliy(rollStrike)
@Test
public void oneStrike() {
    rollStrike();
    game.roll(5);
    game.roll(3);
    rollMany(16, 0);
    assertThat(game.score(), is(26));
}

private void rollStrike() {
    game.roll(10);
}

9. perfectGame

9.1 perfectGame

@Test
public void perfectGame() {
    rollMany(12, 10);
    assertThat(game.score(), is(300));
}
  • red phase์ธ๋ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณตํ•œ๋‹ค. ์™œ ๊ทธ๋Ÿด๊นŒ ?
  • production code๋ฅผ ์‚ดํŽด๋ณด๋‹ˆ 3๊ฐœ์˜ ๊ฒฝ์šฐ ์ˆ˜๊ฐ€ ์žˆ๋Š”๋ฐ, ์‹ค์ œ ๋ณผ๋ง ๊ฒŒ์ž„์—๋„ ๊ทธ 3๊ฐ€์ง€ ๊ฒฝ์šฐ ์ˆ˜๋งŒ ์กด์žฌํ•œ๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published