Skip to content

Commit 894e024

Browse files
author
Jonasz Czerepko
committed
Minor refactoring of the bowling kata:
- 'frame' package encapsulation - flattened obsolete abstraction level (GameFrames class) - improved methods naming for better readability
1 parent f9fcb5e commit 894e024

File tree

11 files changed

+129
-112
lines changed

11 files changed

+129
-112
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package io.github.jonarzz.kata.bowling.game;
2+
3+
import io.github.jonarzz.kata.bowling.game.frame.Frame;
4+
import io.github.jonarzz.kata.bowling.game.frame.FrameFactory;
5+
import io.github.jonarzz.kata.bowling.game.frame.Roll;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
public class DefaultGame implements Game {
11+
12+
public static final int MAX_FRAMES = 10;
13+
14+
private final FrameFactory frameFactory = new FrameFactory();
15+
16+
private final List<Frame> frames = new ArrayList<>(MAX_FRAMES);
17+
18+
private int rollsCount;
19+
20+
@Override
21+
public void roll(int knockedDownPins) {
22+
Frame frame;
23+
Roll roll;
24+
do {
25+
roll = Roll.number(++rollsCount)
26+
.knockDown(knockedDownPins);
27+
frame = frameFactory.nextFrame(roll)
28+
.orElseGet(this::getCurrentFrame);
29+
} while (!frame.offerRoll(roll));
30+
31+
if (frames.isEmpty() || frame != getCurrentFrame()) {
32+
frames.add(frame);
33+
}
34+
}
35+
36+
@Override
37+
public int score() throws IncompleteGameException {
38+
var framesCount = frames.size();
39+
if (framesCount < MAX_FRAMES) {
40+
throw new IncompleteGameException();
41+
}
42+
var points = 0;
43+
for (int index = 0; index < framesCount; index++) {
44+
points += frames.get(index)
45+
.pointsTotal(frames.subList(index + 1,
46+
framesCount));
47+
}
48+
return points;
49+
}
50+
51+
private Frame getCurrentFrame() {
52+
return frames.get(frames.size() - 1);
53+
}
54+
55+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package io.github.jonarzz.kata.bowling.game;
22

3-
public class IncompleteGameException extends RuntimeException{
3+
public class IncompleteGameException extends RuntimeException {
44

55
}

bowling-game/src/main/java/io/github/jonarzz/kata/bowling/game/frame/DefaultGame.java

Lines changed: 0 additions & 20 deletions
This file was deleted.

bowling-game/src/main/java/io/github/jonarzz/kata/bowling/game/frame/Frame.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.util.List;
44
import java.util.Optional;
55

6-
interface Frame {
6+
public interface Frame {
77

88
boolean offerRoll(Roll roll);
99

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.github.jonarzz.kata.bowling.game.frame;
2+
3+
import static io.github.jonarzz.kata.bowling.game.DefaultGame.MAX_FRAMES;
4+
5+
import java.util.Optional;
6+
7+
public class FrameFactory {
8+
9+
static final int ROLLS_PER_STANDARD_FRAME = 2;
10+
static final int MAX_ROLLS_PER_LAST_FRAME = 3;
11+
12+
static final int MAX_PINS_PER_FRAME = 10;
13+
14+
private static final int LAST_STANDARD_FRAME_ROLL_NUMBER = (MAX_FRAMES - 1) * ROLLS_PER_STANDARD_FRAME;
15+
private static final int MAX_TOTAL_ROLLS = LAST_STANDARD_FRAME_ROLL_NUMBER + MAX_ROLLS_PER_LAST_FRAME;
16+
17+
public Optional<Frame> nextFrame(Roll current) {
18+
var currentRollNumber = current.number();
19+
if (isRollOutOfBounds(currentRollNumber)) {
20+
return Optional.of(new NoOpFrame());
21+
}
22+
if (isSecondRollInFrame(currentRollNumber) || isLastRoll(currentRollNumber)) {
23+
return Optional.empty();
24+
}
25+
if (isStandardFrameRoll(currentRollNumber)) {
26+
return Optional.of(new StandardFrame());
27+
}
28+
return Optional.of(new LastFrame());
29+
}
30+
31+
private boolean isRollOutOfBounds(int currentRollNumber) {
32+
return currentRollNumber > MAX_TOTAL_ROLLS;
33+
}
34+
35+
private boolean isSecondRollInFrame(int currentRollNumber) {
36+
return currentRollNumber % ROLLS_PER_STANDARD_FRAME == 0;
37+
}
38+
39+
private boolean isLastRoll(int currentRollNumber) {
40+
return currentRollNumber == MAX_TOTAL_ROLLS;
41+
}
42+
43+
private boolean isStandardFrameRoll(int currentRollNumber) {
44+
return currentRollNumber <= LAST_STANDARD_FRAME_ROLL_NUMBER;
45+
}
46+
47+
}

bowling-game/src/main/java/io/github/jonarzz/kata/bowling/game/frame/GameFrames.java

Lines changed: 0 additions & 67 deletions
This file was deleted.

bowling-game/src/main/java/io/github/jonarzz/kata/bowling/game/frame/LastFrame.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package io.github.jonarzz.kata.bowling.game.frame;
22

3-
import static io.github.jonarzz.kata.bowling.game.frame.GameFrames.ROLLS_PER_STANDARD_FRAME;
3+
import static io.github.jonarzz.kata.bowling.game.frame.FrameFactory.ROLLS_PER_STANDARD_FRAME;
44

55
import java.util.List;
66

77
class LastFrame extends ScoredFrame {
88

99
@Override
1010
boolean shouldAccept(Roll roll) {
11-
if (rolls.size() == ROLLS_PER_STANDARD_FRAME) {
11+
var rollsCount = rolls.size();
12+
if (rollsCount == ROLLS_PER_STANDARD_FRAME) {
1213
return knockedDownPinsTotal() >= 10;
1314
}
14-
return rolls.size() < ROLLS_PER_STANDARD_FRAME;
15+
return rollsCount < ROLLS_PER_STANDARD_FRAME;
1516
}
1617

1718
@Override

bowling-game/src/main/java/io/github/jonarzz/kata/bowling/game/frame/Roll.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.github.jonarzz.kata.bowling.game.frame;
22

3-
class Roll {
3+
import static io.github.jonarzz.kata.bowling.game.frame.FrameFactory.MAX_PINS_PER_FRAME;
44

5-
static final int MAX_PINS = 10;
5+
public class Roll {
66

77
private int number;
88
private int knockedDownPins;
@@ -12,7 +12,7 @@ private Roll(int number, int knockedDownPins) {
1212
this.knockedDownPins = knockedDownPins;
1313
}
1414

15-
static RollCreator number(int number) {
15+
public static RollCreator number(int number) {
1616
return new RollCreator(number);
1717
}
1818

@@ -25,20 +25,20 @@ int knockedDownPins() {
2525
}
2626

2727
boolean isStrike() {
28-
return MAX_PINS == knockedDownPins;
28+
return MAX_PINS_PER_FRAME == knockedDownPins;
2929
}
3030

31-
static class RollCreator {
31+
public static class RollCreator {
3232

3333
private int rollNumber;
3434

3535
private RollCreator(int rollNumber) {
3636
this.rollNumber = rollNumber;
3737
}
3838

39-
Roll knockDown(int pins) {
40-
if (pins > MAX_PINS) {
41-
throw new IllegalArgumentException("Max pins that can be knocked down in one roll is " + MAX_PINS);
39+
public Roll knockDown(int pins) {
40+
if (pins > MAX_PINS_PER_FRAME) {
41+
throw new IllegalArgumentException("Max pins that can be knocked down in one roll is " + MAX_PINS_PER_FRAME);
4242
}
4343
return new Roll(rollNumber, pins);
4444
}

bowling-game/src/main/java/io/github/jonarzz/kata/bowling/game/frame/ScoredFrame.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ public final boolean offerRoll(Roll roll) {
1919

2020
@Override
2121
public final Roll firstRoll() {
22-
return firstElement(rolls);
22+
return firstElementFrom(rolls);
2323
}
2424

2525
@Override
2626
public final Optional<Roll> secondRoll() {
27-
return secondElement(rolls);
27+
return secondElementFrom(rolls);
2828
}
2929

3030
abstract boolean shouldAccept(Roll roll);
@@ -35,11 +35,11 @@ final int knockedDownPinsTotal() {
3535
.sum();
3636
}
3737

38-
static <T> T firstElement(List<T> elements) {
38+
static <T> T firstElementFrom(List<T> elements) {
3939
return elements.get(0);
4040
}
4141

42-
static <T> Optional<T> secondElement(List<T> elements) {
42+
static <T> Optional<T> secondElementFrom(List<T> elements) {
4343
return elements.stream()
4444
.skip(1)
4545
.findFirst();

bowling-game/src/main/java/io/github/jonarzz/kata/bowling/game/frame/StandardFrame.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.github.jonarzz.kata.bowling.game.frame;
22

3-
import static io.github.jonarzz.kata.bowling.game.frame.GameFrames.ROLLS_PER_STANDARD_FRAME;
3+
4+
import static io.github.jonarzz.kata.bowling.game.frame.FrameFactory.MAX_PINS_PER_FRAME;
5+
import static io.github.jonarzz.kata.bowling.game.frame.FrameFactory.ROLLS_PER_STANDARD_FRAME;
46

57
import java.util.ArrayList;
68
import java.util.List;
@@ -10,16 +12,16 @@ class StandardFrame extends ScoredFrame {
1012
@Override
1113
public int pointsTotal(List<Frame> followingFrames) {
1214
var knockedDownPins = knockedDownPinsTotal();
13-
if (knockedDownPins < Roll.MAX_PINS) {
15+
if (knockedDownPins < MAX_PINS_PER_FRAME) {
1416
return knockedDownPins;
1517
}
1618
var scoredRolls = new ArrayList<>(rolls);
17-
var nextFrame = firstElement(followingFrames);
19+
var nextFrame = firstElementFrom(followingFrames);
1820
scoredRolls.add(nextFrame.firstRoll());
1921
if (firstRoll().isStrike()) {
2022
nextFrame.secondRoll()
2123
.ifPresentOrElse(scoredRolls::add,
22-
() -> secondElement(followingFrames)
24+
() -> secondElementFrom(followingFrames)
2325
.map(Frame::firstRoll)
2426
.ifPresent(scoredRolls::add));
2527
}
@@ -34,11 +36,11 @@ boolean shouldAccept(Roll roll) {
3436
return false;
3537
}
3638
var knockedDownPins = knockedDownPinsTotal();
37-
if (knockedDownPins == Roll.MAX_PINS) {
39+
if (knockedDownPins == MAX_PINS_PER_FRAME) {
3840
return false;
3941
}
40-
if (knockedDownPins + roll.knockedDownPins() > Roll.MAX_PINS) {
41-
throw new IllegalArgumentException("Max pins that can be knocked down in one frame is " + Roll.MAX_PINS);
42+
if (knockedDownPins + roll.knockedDownPins() > MAX_PINS_PER_FRAME) {
43+
throw new IllegalArgumentException("Max pins that can be knocked down in one frame is " + MAX_PINS_PER_FRAME);
4244
}
4345
return true;
4446
}

0 commit comments

Comments
 (0)