├── .gitignore ├── README.md ├── src ├── core │ └── NaturalNumber.java └── ladder │ ├── LadderGame.java │ ├── LadderGameFactory.java │ ├── LadderRunner.java │ ├── LadderSize.java │ ├── Marker.java │ ├── Node.java │ ├── Position.java │ ├── RandomNaturalNumber.java │ ├── Row.java │ └── creator │ ├── LadderCreator.java │ ├── ManualLadderCreator.java │ └── RandomLadderCreator.java └── test ├── core ├── ArrayListTest.java └── NaturalNumberTest.java └── ladder ├── LadderGameTest.java ├── LadderRunnerTest.java ├── MarkerTest.java ├── NodeTest.java ├── PositionTest.java ├── RandomNaturalNumberTest.java ├── RowTest.java └── creator └── RandomLadderCreatorTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | out/ 4 | require.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 자바지기 박재성님 TDD강의 2 | > 다음 달에 [코드스쿼드 TDD, 리팩토링 강의](http://codesquad.kr/page/specialTdd.html)를 수강하기 전에 유투브에서 자바지기님의 사다리게임을 구현하는 TDD 강좌가 있어서 정주행 하게 되었다. 3 | 4 | 5 | ### 사다리 게임 강좌 목록 6 | ##### 0. 요구사항 분석 7 | - 요구사항 분석 8 | 9 | ##### 1. 일단 사다리 게임 구현해 보기 10 | - step1-1 11 | - step1-2 12 | - step1-3 13 | 14 | ##### 2. 사다리게임 구현 15 | - step2-1 사다리 좌우 이동 기능 구현 16 | - step2-2 사다리 여러행 추가, 2차원 배열로 바꿈 17 | - step2-3 리팩토링 (Row클래스 추출, if문 조건확인 로직 메소드 추출) 18 | 19 | ##### 3. 리팩토링 20 | - step3-1 Ladder Test를 RowTest로 이동 / Row의 moveRow -> move로 rename /moveRow() 메서드 리팩토링 부분을 찾아보기 21 | - step3-2 isLeftDirection메서드명 isRightDirection rename / -1, 0, 1 상수값을 enum 타입으로 정의 22 | - step3-3 Ladder, Row 클래스, 필드, 메소드 접근제어자 처리 23 | - step3-4 Ladder,Row 클래스 예외처리 (생성자 drawLine메서드) 24 | 25 | ##### 4. 객체 추출 리팩토링 26 | - step4-1 NaturalNumber 클래스 추가, Ladder, Row클래스에서 사용하던 int형 값들을 NaturalNumber객체 값으로 리팩토링 27 | - step4-2 좌우 이동 기능을 하는 Marker 클래스 추가(단일 책임) Ladder클래스 run메소드, Row클래스 move메소드에서 Marker 사용하도록 리팩토링 28 | - step4-3 현재 위치의 상태를 나타내는 Node 클래스 추가 Row내 Direction Enum클래스 제거 29 | - step4-4 리팩토링 패키지, 접근제어자 정리, 소스 가독성을 위한 메소드 추출 작업 30 | 31 | ##### 5. 디버깅 로그메시지 출력기능 32 | - step5-1 디버깅 로그 메시지 출력 기능(StringBuilder 사용) 33 | - step5-2 디버깅 로그메시지 출력기능 리팩토링, 이중for문 제거, if else문 enum처리 34 | - step5-3 디버깅 로그메시지 출력기능 리팩토링, height, persons쌍 Position 객체 추출 35 | 36 | ##### 6. 클래스 분리 및 선 Random 생성 37 | - step6-1 SOLID원칙 중 단일책임의 원칙 적용. 클래스 분리 Ladder -> LadderGame, LadderRunner, LadderCreator 분리 38 | - step6-2 RandomLadderCreator 클래스 생성, Random 생성시 발생하는 요구사항 분석 39 | - step6-3 총 position 수 구하기, 사다리 크기에 따라 ratio값에 따라 생성되는 선의 수를 결정 사다리 생성시 크기값을 담당하는 객체 추출 int값을 2차원 배열 위치 값으로 변환 40 | - step6-4 LadderSize 클래스 추출 41 | - step6-5 RandomLadderCreator 랜덤값 유효성 검증 42 | 43 | ##### 7. 인터페이스 추출 및 DI 44 | - step7-1 LadderGame에 RandomLadderCreator 붙이기 45 | - step7-2 ManualLadderCreator, RandomLadderCreator -> LadderCreator 인터페이스 분리 46 | - step7-3 LadderCreate 기능을 LadderCreatorFactory에 위임, ladder.creator패키지 생성 및 접근 제어자 수정 47 | - step7-4 LadderCreator DI 적용 48 | - step7-5 상속을 통한 RandomLadderCreator 중복 제거 49 | - step7-6 조합을 통한 RandomLadderCreator 중복 제거 50 | 51 | ##### 8. Collection을 사용한 리팩토링 52 | - step8-1 리팩토링 RandomNaturalNumber 생성 53 | - step8-2 리팩토링 RandomLadderCreator Array사용하는 부분 -> Collection의 List로 변경 54 | - step8-3 리팩토링 RandomLadderCreator NaturalNumber isFirst() 구현 55 | - step8-4 RandomLadderCreator클래스의 generateRandomPositions() 복잡한 로직을 Collection비교 방법으로 리팩토링 56 | 57 | #### 후기 58 | - 일주일간 틈틈이 한강좌씩 따라하다 보니 TDD방식으로 코딩하는게 이런거다하는 몸소 느낄 수 있었다. 59 | - 테스트 클래스가 있다보니 리팩토링 할 때 마음 놓고 수정할 수 있어서 if else문으로 짜여진 복잡도가 높은 로직도 step by step으로 리팩토링이 가능했다. 실패하던 빨간색 테스트 케이스를 실행해서 녹색불이 들어왔을 때 짜릿한 성취감을 느낄 수 있었다. 60 | - 기능단위의 test클래스를 먼저 만들고 기능을 구현하다보니 자연스럽게 SOLID원칙 중에 단일책임의 원칙을 생각하면서 클래스를 만들 수 있게 되었다. 나머지 다른 원칙들도 어떻게 적용되는지 공부가 더 필요하다. 61 | - 사전 과제로 내주신 기본적인 git 사용법을 익히기 위해 source tree나 ide에서 제공하는 git 기능을 사용하지 않고 terminal에서 git명령어를 직접 사용하였다. commit로그도 최대한 상세하게 남겼다. oh-my-zsh을 설치해서 사용하니 터미널에서도 이쁜 commit 로그를 볼 수 있어서 좋았다. 62 | -------------------------------------------------------------------------------- /src/core/NaturalNumber.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import java.util.Objects; 4 | 5 | public class NaturalNumber { 6 | 7 | private static final int INTERVAL = 1; 8 | private static final int FIRST_OF_NATURAL_NUMBER = 1; 9 | private int number; 10 | 11 | public NaturalNumber(int number) { 12 | if (number < 1) { 13 | throw new IllegalArgumentException(String.format("자연수는 1 이상이어야 합니다. 현재 값은 : %d", number)); 14 | } 15 | this.number = number; 16 | } 17 | 18 | public int getNumber() { 19 | return this.number; 20 | } 21 | 22 | public int toArrayIndex() { 23 | return this.number - INTERVAL; 24 | } 25 | 26 | public NaturalNumber multiply(NaturalNumber operand) { 27 | return new NaturalNumber(this.number * operand.number); 28 | } 29 | 30 | public static NaturalNumber createFromArrayIndex(int index) { 31 | return new NaturalNumber(index + INTERVAL); 32 | } 33 | 34 | public boolean isFirst() { 35 | return isFirst(getNumber()); 36 | } 37 | 38 | public boolean isFirst(int number) { 39 | return number == FIRST_OF_NATURAL_NUMBER; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object o) { 44 | if (this == o) { 45 | return true; 46 | } 47 | if (!(o instanceof NaturalNumber)) { 48 | return false; 49 | } 50 | NaturalNumber that = (NaturalNumber) o; 51 | return number == that.number; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | 57 | return Objects.hash(number); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return number + ""; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ladder/LadderGame.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import ladder.creator.LadderCreator; 5 | 6 | class LadderGame { 7 | 8 | private LadderCreator ladderCreator; 9 | 10 | public LadderGame(LadderCreator ladderCreator) { 11 | this.ladderCreator = ladderCreator; 12 | } 13 | 14 | void drawLine(NaturalNumber height, NaturalNumber startPosition) { 15 | ladderCreator.drawLine(height, startPosition); 16 | } 17 | 18 | Marker run(Marker nthOfPerson) { 19 | LadderRunner runner = new LadderRunner(ladderCreator.getLadder()); 20 | return runner.run(nthOfPerson); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ladder/LadderGameFactory.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import ladder.creator.ManualLadderCreator; 5 | import ladder.creator.RandomLadderCreator; 6 | 7 | public class LadderGameFactory { 8 | 9 | private LadderGameFactory() { 10 | } 11 | 12 | static LadderGame randomLadderGame(NaturalNumber height, NaturalNumber noOfPerson) { 13 | ManualLadderCreator manualLadderCreator = new ManualLadderCreator(height, noOfPerson); 14 | RandomLadderCreator ladderCreator = new RandomLadderCreator(manualLadderCreator); 15 | return new LadderGame(ladderCreator); 16 | } 17 | 18 | static LadderGame manualLadderGame(NaturalNumber height, NaturalNumber noOfPerson) { 19 | ManualLadderCreator ladderCreator = new ManualLadderCreator(height, noOfPerson); 20 | return new LadderGame(ladderCreator); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ladder/LadderRunner.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | class LadderRunner { 4 | 5 | private Row[] rows; 6 | 7 | LadderRunner(Row[] rows) { 8 | this.rows = rows; 9 | } 10 | 11 | Marker run(Marker nthOfPerson) { 12 | for (int i = 0; i < rows.length; i++) { 13 | Row row = rows[i]; 14 | System.out.println("Before : "); 15 | System.out.println(generate(rows, Position.createFromArrayIndex(i, nthOfPerson.toArrayIndex()))); 16 | nthOfPerson = row.move(nthOfPerson); 17 | System.out.println("After : "); 18 | System.out.println(generate(rows, Position.createFromArrayIndex(i, nthOfPerson.toArrayIndex()))); 19 | } 20 | return nthOfPerson; 21 | } 22 | 23 | static String generate(Row[] rows, Position position) { 24 | StringBuilder sb = new StringBuilder(); 25 | for (int i = 0; i < rows.length; i++) { 26 | Row row = rows[i]; 27 | row.generateRow(sb, i, position); 28 | } 29 | 30 | return sb.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ladder/LadderSize.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | 5 | public class LadderSize { 6 | 7 | private NaturalNumber height; 8 | private NaturalNumber noOfPerson; 9 | 10 | private LadderSize(NaturalNumber height, NaturalNumber noOfPerson) { 11 | this.height = height; 12 | this.noOfPerson = noOfPerson; 13 | } 14 | 15 | public NaturalNumber getHeight() { 16 | return height; 17 | } 18 | 19 | public NaturalNumber getNoOfPerson() { 20 | return noOfPerson; 21 | } 22 | 23 | static LadderSize create(int height, int noOfPerson) { 24 | return new LadderSize(new NaturalNumber(height), new NaturalNumber(noOfPerson)); 25 | } 26 | 27 | public static LadderSize create(NaturalNumber height, NaturalNumber nthOfPersion) { 28 | return new LadderSize(height, nthOfPersion); 29 | } 30 | 31 | public Position getPosition(RandomNaturalNumber randomNaturalNumber) { 32 | int positionOfHeight = randomNaturalNumber.getPositionOfHeight(noOfPerson.getNumber()); 33 | int positionOfPerson = randomNaturalNumber.getPositionOfPerson(noOfPerson.getNumber()); 34 | return Position.create(positionOfHeight, positionOfPerson); 35 | } 36 | 37 | public int getCountOfLine(double ratio) { 38 | NaturalNumber totalPositions = getTotalPosition(); 39 | return (int) (totalPositions.getNumber() * ratio); 40 | } 41 | 42 | public NaturalNumber getTotalPosition() { 43 | return height.multiply(noOfPerson); 44 | } 45 | 46 | public boolean isMultipleOfPerson(RandomNaturalNumber randomPosition) { 47 | return randomPosition.isMultipleOfPerson(noOfPerson.getNumber()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ladder/Marker.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | 5 | class Marker extends NaturalNumber { 6 | 7 | Marker(int number) { 8 | super(number); 9 | } 10 | 11 | public Marker moveRight() { 12 | return new Marker(getNumber() + 1); 13 | } 14 | 15 | public Marker moveLeft() { 16 | return new Marker(getNumber() - 1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ladder/Node.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import java.util.Objects; 4 | 5 | public class Node { 6 | 7 | enum Direction { 8 | LEFT(-1), CENTER(0), RIGHT(1); 9 | private int symbol; 10 | 11 | private Direction(int symbol) { 12 | this.symbol = symbol; 13 | } 14 | } 15 | 16 | private Direction direction; 17 | 18 | private Node(Direction direction) { 19 | this.direction = direction; 20 | } 21 | 22 | Direction getDirection() { 23 | return direction; 24 | } 25 | 26 | void changeRight() { 27 | this.direction = Direction.RIGHT; 28 | } 29 | 30 | void changeLeft() { 31 | this.direction = Direction.LEFT; 32 | } 33 | 34 | boolean isRightDirection() { 35 | return direction == Direction.RIGHT; 36 | } 37 | 38 | boolean isLeftDirection() { 39 | return direction == Direction.LEFT; 40 | } 41 | 42 | void appendSymbol(StringBuilder builder) { 43 | builder.append(direction.symbol); 44 | } 45 | 46 | Marker move(Marker marker) { 47 | if (isRightDirection()) { 48 | return marker.moveRight(); 49 | } 50 | 51 | if (isLeftDirection()) { 52 | return marker.moveLeft(); 53 | } 54 | 55 | return marker; 56 | } 57 | 58 | static Node createCenterNode() { 59 | return new Node(Direction.CENTER); 60 | } 61 | 62 | static Node createRightNode() { 63 | return new Node(Direction.RIGHT); 64 | } 65 | 66 | static Node createLeftNode() { 67 | return new Node(Direction.LEFT); 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) { 73 | return true; 74 | } 75 | if (o == null || getClass() != o.getClass()) { 76 | return false; 77 | } 78 | Node node = (Node) o; 79 | return direction == node.direction; 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return Objects.hash(direction); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/ladder/Position.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import java.util.Objects; 5 | 6 | public class Position { 7 | 8 | private NaturalNumber height; 9 | private NaturalNumber nthOfPerson; 10 | 11 | private Position(NaturalNumber height, NaturalNumber nthOfPerson) { 12 | this.height = height; 13 | this.nthOfPerson = nthOfPerson; 14 | } 15 | 16 | static Position create(int height, int nthOfPerson) { 17 | return create(new NaturalNumber(height), new NaturalNumber(nthOfPerson)); 18 | } 19 | 20 | static Position create(NaturalNumber height, NaturalNumber nthOfPersion) { 21 | return new Position(height, nthOfPersion); 22 | } 23 | 24 | static Position createFromArrayIndex(int height, int nthOfPerson) { 25 | return new Position(NaturalNumber.createFromArrayIndex(height), NaturalNumber.createFromArrayIndex(nthOfPerson)); 26 | } 27 | 28 | public NaturalNumber getHeight() { 29 | return height; 30 | } 31 | 32 | public NaturalNumber getNthOfPerson() { 33 | return nthOfPerson; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (this == o) { 39 | return true; 40 | } 41 | if (o == null || getClass() != o.getClass()) { 42 | return false; 43 | } 44 | Position position = (Position) o; 45 | return Objects.equals(height, position.height) && 46 | Objects.equals(nthOfPerson, position.nthOfPerson); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | return Objects.hash(height, nthOfPerson); 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "Position{" + 57 | "height=" + height + 58 | ", nthOfPerson=" + nthOfPerson + 59 | '}'; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/ladder/RandomNaturalNumber.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class RandomNaturalNumber extends NaturalNumber { 8 | 9 | public RandomNaturalNumber(int number) { 10 | super(number); 11 | } 12 | 13 | public int getPositionOfPerson(int noOfPerson) { 14 | int remainder = this.getNumber() % noOfPerson; 15 | if (remainder == 0) { 16 | return noOfPerson; 17 | } 18 | return remainder; 19 | } 20 | 21 | public int getPositionOfHeight(int noOfPerson) { 22 | double ceilDevideEnd = Math.ceil(this.getNumber() / (double) noOfPerson); 23 | return new Double(ceilDevideEnd).intValue(); 24 | } 25 | 26 | public boolean isMultipleOfPerson(int noOfPerson) { 27 | int remainder = this.getNumber() % noOfPerson; 28 | if (remainder == 0) { 29 | return true; 30 | } 31 | return false; 32 | } 33 | 34 | public RandomNaturalNumber before() { 35 | return new RandomNaturalNumber(toArrayIndex()); 36 | } 37 | 38 | public RandomNaturalNumber next() { 39 | return new RandomNaturalNumber(getNumber() + 1); 40 | } 41 | 42 | public List checkedNaturalNumbers(NaturalNumber noOfPerson) { 43 | List naturalNumbers = new ArrayList<>(); 44 | 45 | int remainder = this.getNumber() % noOfPerson.getNumber(); 46 | if (!isFirst(remainder)) { 47 | naturalNumbers.add(before()); 48 | } 49 | naturalNumbers.add(this); 50 | naturalNumbers.add(next()); 51 | return naturalNumbers; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/ladder/Row.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | 5 | public class Row { 6 | private Node[] nodes; 7 | 8 | public Row(NaturalNumber noOfPerson) { 9 | nodes = new Node[noOfPerson.getNumber()]; 10 | for (int i = 0; i < nodes.length; i++) { 11 | nodes[i] = Node.createCenterNode(); 12 | } 13 | } 14 | 15 | public void drawLine(NaturalNumber startPosition) { 16 | int startIndex = startPosition.toArrayIndex(); 17 | if (isOverNoOfPersons(startIndex)) { 18 | throw new IllegalArgumentException(String.format("시작점 Index값은 %d 미만이어야 합니다. 현재 값 : %d", nodes.length - 1, startIndex)); 19 | } 20 | 21 | if (nodes[startIndex].isLeftDirection()) { 22 | throw new IllegalArgumentException("선을 그을 수 없는 위치입니다."); 23 | } 24 | 25 | nodes[startIndex].changeRight(); 26 | nodes[startIndex + 1].changeLeft(); 27 | } 28 | 29 | private boolean isOverNoOfPersons(int startIndex) { 30 | // 사람수 5일경우 선 시작 위치는 4이상이 될 수 없다. 즉 3까지 허용 31 | return startIndex >= nodes.length - 1; 32 | } 33 | 34 | Marker move(Marker marker) { 35 | return nodes[marker.toArrayIndex()].move(marker); 36 | } 37 | 38 | // public Node[] getNodes() { 39 | // return this.nodes; 40 | // } 41 | 42 | void generateRow(StringBuilder sb, int currentHeight, Position position) { 43 | for (int j = 0; j < nodes.length; j++) { 44 | Node node = nodes[j]; 45 | node.appendSymbol(sb); 46 | 47 | if (position.equals(Position.createFromArrayIndex(currentHeight, j))) { 48 | sb.append("*"); 49 | } 50 | 51 | sb.append(" "); 52 | } 53 | sb.append("\n"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ladder/creator/LadderCreator.java: -------------------------------------------------------------------------------- 1 | package ladder.creator; 2 | 3 | import core.NaturalNumber; 4 | import ladder.LadderSize; 5 | import ladder.Row; 6 | 7 | public interface LadderCreator { 8 | 9 | void drawLine(NaturalNumber height, NaturalNumber startPosition); 10 | 11 | Row[] getLadder(); 12 | 13 | LadderSize getLadderSize(); 14 | } 15 | -------------------------------------------------------------------------------- /src/ladder/creator/ManualLadderCreator.java: -------------------------------------------------------------------------------- 1 | package ladder.creator; 2 | 3 | import core.NaturalNumber; 4 | import ladder.LadderSize; 5 | import ladder.Row; 6 | 7 | public class ManualLadderCreator implements LadderCreator { 8 | 9 | private Row[] rows; 10 | private LadderSize ladderSize; 11 | 12 | public ManualLadderCreator(NaturalNumber height, NaturalNumber noOfPerson) { 13 | rows = new Row[height.getNumber()]; 14 | ladderSize = LadderSize.create(height, noOfPerson); 15 | for (int i = 0; i < height.getNumber(); i++) { 16 | rows[i] = new Row(noOfPerson); 17 | } 18 | } 19 | 20 | public LadderSize getLadderSize() { 21 | return ladderSize; 22 | } 23 | 24 | @Override 25 | public void drawLine(NaturalNumber height, NaturalNumber startPosition) { 26 | if (isOverHeight(height)) { 27 | throw new IllegalArgumentException( 28 | String.format("사다리 최대 높이를 넘어섰습니다.현재 값은 : %d", height.getNumber())); 29 | } 30 | 31 | rows[height.toArrayIndex()].drawLine(startPosition); 32 | } 33 | 34 | @Override 35 | public Row[] getLadder() { 36 | return this.rows; 37 | } 38 | 39 | private boolean isOverHeight(NaturalNumber height) { 40 | return height.toArrayIndex() > rows.length - 1; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ladder/creator/RandomLadderCreator.java: -------------------------------------------------------------------------------- 1 | package ladder.creator; 2 | 3 | import core.NaturalNumber; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Random; 7 | import ladder.LadderSize; 8 | import ladder.Position; 9 | import ladder.RandomNaturalNumber; 10 | import ladder.Row; 11 | 12 | public class RandomLadderCreator implements LadderCreator { 13 | 14 | private static final double DEFAULT_LINE_RATIO = 0.3; 15 | private LadderCreator ladderCreator; 16 | private LadderSize ladderSize; 17 | 18 | public RandomLadderCreator(LadderCreator ladderCreator) { 19 | this.ladderCreator = ladderCreator; 20 | this.ladderSize = ladderCreator.getLadderSize(); 21 | List startPositions = generateStartPositions(); 22 | for (Position position : startPositions) { 23 | ladderCreator.drawLine(position.getHeight(), position.getNthOfPerson()); 24 | } 25 | } 26 | 27 | @Override 28 | public void drawLine(NaturalNumber height, NaturalNumber startPosition) { 29 | throw new UnsupportedOperationException("RandomLadderCreator에서는 drawLine 메서드를 호출할 수 없습니다."); 30 | } 31 | 32 | @Override 33 | public Row[] getLadder() { 34 | return this.ladderCreator.getLadder(); 35 | } 36 | 37 | @Override 38 | public LadderSize getLadderSize() { 39 | return this.ladderSize; 40 | } 41 | 42 | List generateStartPositions() { 43 | List numbers = generateRandomPositions(); 44 | return toPositions(numbers); 45 | } 46 | 47 | List generateRandomPositions() { 48 | NaturalNumber totalPositions = ladderSize.getTotalPosition(); 49 | int countOfLine = ladderSize.getCountOfLine(DEFAULT_LINE_RATIO); 50 | List randomPositions = new ArrayList<>(); 51 | 52 | do { 53 | RandomNaturalNumber randomPosition = randInt(1, totalPositions.getNumber()); 54 | if (ladderSize.isMultipleOfPerson(randomPosition)) { 55 | continue; 56 | } 57 | 58 | // 아래 로직을 Collection비교를 통한 방법으로 리팩토링 59 | List checkedNaturalNumbers = randomPosition.checkedNaturalNumbers(ladderSize.getNoOfPerson()); 60 | checkedNaturalNumbers.retainAll(randomPositions); 61 | 62 | // 체크넘버에 포함되어 있지 않으면 추가 (retainAll은 교집합 추출) 63 | if (checkedNaturalNumbers.isEmpty()) { 64 | randomPositions.add(randomPosition); 65 | System.out.println(String.format("random position : %s", randomPosition)); 66 | } 67 | 68 | // if (randomPositions.contains(randomPosition)) { 69 | // continue; 70 | // } 71 | // 72 | // if (randomPositions.contains(new RandomNaturalNumber(randomPosition.getNumber() + 1))) { 73 | // continue; 74 | // } 75 | // 76 | // if (randomPosition.isFirst()) { 77 | // randomPositions.add(randomPosition); 78 | // System.out.println(String.format("random position : %s", randomPosition)); 79 | // } else { 80 | // if (randomPositions.contains(new RandomNaturalNumber(randomPosition.toArrayIndex()))) { 81 | // continue; 82 | // } 83 | // 84 | // randomPositions.add(randomPosition); 85 | // System.out.println(String.format("random position : %s", randomPosition)); 86 | // } 87 | } while (randomPositions.size() < countOfLine); 88 | 89 | return randomPositions; 90 | } 91 | 92 | List toPositions(List randomNumbers) { 93 | List positions = new ArrayList<>(randomNumbers.size()); 94 | for (RandomNaturalNumber randomNumber : randomNumbers) { 95 | positions.add(ladderSize.getPosition(randomNumber)); 96 | //System.out.println("ladderSize.getPosition(randomNumber) = " + ladderSize.getPosition(randomNumber)); 97 | } 98 | return positions; 99 | } 100 | 101 | static RandomNaturalNumber randInt(int min, int max) { 102 | Random rand = new Random(); 103 | return new RandomNaturalNumber(rand.nextInt((max - min) + 1) + min); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /test/core/ArrayListTest.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import junit.framework.TestCase; 6 | 7 | public class ArrayListTest extends TestCase { 8 | 9 | public void testRetainAll() { 10 | List values = new ArrayList<>(); 11 | values.add(1); 12 | values.add(2); 13 | values.add(3); 14 | List values2 = new ArrayList<>(); 15 | values2.add(2); 16 | values2.add(3); 17 | 18 | values.retainAll(values2); 19 | //System.out.println(values); 20 | //System.out.println(values2); 21 | assertTrue(values.size() == 2); 22 | } 23 | 24 | public void testRetainAll2() { 25 | List values = new ArrayList<>(); 26 | values.add(1); 27 | values.add(2); 28 | values.add(3); 29 | List values2 = new ArrayList<>(); 30 | values2.add(4); 31 | 32 | values.retainAll(values2); 33 | //System.out.println(values); 34 | //System.out.println(values2); 35 | assertTrue(values.size() == 0); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/core/NaturalNumberTest.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import junit.framework.TestCase; 4 | import ladder.RandomNaturalNumber; 5 | 6 | public class NaturalNumberTest extends TestCase { 7 | 8 | public void testCreate() { 9 | NaturalNumber number = new NaturalNumber(1); 10 | assertEquals(1, number.getNumber()); 11 | } 12 | 13 | public void testCreateWhenUnderZero() { 14 | try { 15 | NaturalNumber number = new NaturalNumber(0); 16 | fail("IllegalArgumentException 에러가 발생해야 한다."); 17 | } catch (IllegalArgumentException e) { 18 | assertTrue(true); 19 | } 20 | } 21 | 22 | public void testToArrayIndex() { 23 | NaturalNumber number = new NaturalNumber(3); 24 | assertEquals(2, number.toArrayIndex()); 25 | } 26 | 27 | public void testCreateFromArrayIndex() { 28 | NaturalNumber actual = NaturalNumber.createFromArrayIndex(1); 29 | assertEquals(new NaturalNumber(2), actual); 30 | } 31 | 32 | public void testMultiply() { 33 | NaturalNumber number = new NaturalNumber(3); 34 | NaturalNumber actual = number.multiply(new NaturalNumber(4)); 35 | assertEquals(new NaturalNumber(12), actual); 36 | } 37 | 38 | public void testIsFirst() { 39 | NaturalNumber number = new NaturalNumber(1); 40 | assertTrue(number.isFirst()); 41 | 42 | number = new NaturalNumber(2); 43 | assertFalse(number.isFirst()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/ladder/LadderGameTest.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import junit.framework.TestCase; 5 | 6 | public class LadderGameTest extends TestCase { 7 | 8 | public void testRunBigLadder() { 9 | LadderGame ladderGame = LadderGameFactory.randomLadderGame(new NaturalNumber(4), new NaturalNumber(6)); 10 | Marker result = ladderGame.run(new Marker(2)); 11 | System.out.println(result); 12 | } 13 | 14 | public void testRunWhenRandomLadderAndDrawLine() { 15 | try { 16 | LadderGame ladderGame = LadderGameFactory.randomLadderGame(new NaturalNumber(10), new NaturalNumber(6)); 17 | ladderGame.drawLine(new NaturalNumber(1), new NaturalNumber(1)); 18 | fail(); 19 | } catch (Exception e) { 20 | assertTrue(true); 21 | } 22 | } 23 | 24 | public void testRunWhenRandomLadder() { 25 | LadderGame ladderGame = LadderGameFactory.randomLadderGame(new NaturalNumber(3), new NaturalNumber(4)); 26 | Marker result = ladderGame.run(new Marker(2)); 27 | System.out.println(result); 28 | } 29 | 30 | public void testRunWhenManualLadder() { 31 | // 1 -1 0 0 32 | // 0 1 -1 0 33 | // 0 0 1 -1 34 | LadderGame ladderGame = LadderGameFactory.manualLadderGame(new NaturalNumber(3), new NaturalNumber(4)); 35 | ladderGame.drawLine(new NaturalNumber(1), new NaturalNumber(1)); 36 | ladderGame.drawLine(new NaturalNumber(2), new NaturalNumber(2)); 37 | ladderGame.drawLine(new NaturalNumber(3), new NaturalNumber(3)); 38 | 39 | assertEquals(new Marker(4), ladderGame.run(new Marker(1))); 40 | assertEquals(new Marker(1), ladderGame.run(new Marker(2))); 41 | assertEquals(new Marker(2), ladderGame.run(new Marker(3))); 42 | assertEquals(new Marker(3), ladderGame.run(new Marker(4))); 43 | } 44 | 45 | public void testDrawLineWhenOverNoOfRows() { 46 | try { 47 | LadderGame ladderGame = LadderGameFactory.manualLadderGame(new NaturalNumber(3), new NaturalNumber(4)); 48 | ladderGame.drawLine(new NaturalNumber(4), new NaturalNumber(4)); 49 | fail("IllegalArgumentException 에러가 발생해야 한다."); 50 | } catch (IllegalArgumentException e) { 51 | assertTrue(true); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/ladder/LadderRunnerTest.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import junit.framework.TestCase; 5 | 6 | public class LadderRunnerTest extends TestCase { 7 | public void testGenerateWhenNoLine() { 8 | Row[] rows = new Row[3]; 9 | for (int i = 0; i < rows.length; i++) { 10 | rows[i] = new Row(new NaturalNumber(3)); 11 | } 12 | 13 | String result = LadderRunner.generate(rows, Position.create(1, 1)); 14 | assertEquals("0* 0 0 \n0 0 0 \n0 0 0 \n", result); 15 | } 16 | 17 | public void testGenerateWhenLine() { 18 | Row[] rows = new Row[3]; 19 | for (int i = 0; i < rows.length; i++) { 20 | rows[i] = new Row(new NaturalNumber(3)); 21 | } 22 | rows[0].drawLine(new NaturalNumber(1)); 23 | String result = LadderRunner.generate(rows, Position.create(1, 1)); 24 | assertEquals("1* -1 0 \n0 0 0 \n0 0 0 \n", result); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/ladder/MarkerTest.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class MarkerTest extends TestCase { 6 | 7 | public void testMoveRight() { 8 | Marker marker = new Marker(3); 9 | assertEquals(new Marker(4), marker.moveRight()); 10 | } 11 | 12 | public void testMoveLeft() { 13 | Marker marker = new Marker(3); 14 | assertEquals(new Marker(2), marker.moveLeft()); 15 | } 16 | } -------------------------------------------------------------------------------- /test/ladder/NodeTest.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import junit.framework.TestCase; 4 | 5 | import static ladder.Node.Direction.*; 6 | 7 | public class NodeTest extends TestCase { 8 | 9 | public void testCreateCenter() { 10 | Node node = Node.createCenterNode(); 11 | assertEquals(CENTER, node.getDirection()); 12 | } 13 | 14 | public void testChangeRight() { 15 | Node node = Node.createCenterNode(); 16 | node.changeRight(); 17 | assertEquals(Node.createRightNode(), node); 18 | } 19 | 20 | public void testChangeLeft() { 21 | Node node = Node.createCenterNode(); 22 | node.changeLeft(); 23 | assertEquals(Node.createLeftNode(), node); 24 | } 25 | 26 | public void testMoveRight() { 27 | Node node = Node.createRightNode(); 28 | Marker marker = node.move(new Marker(3)); 29 | assertEquals(new Marker(4), marker); 30 | } 31 | 32 | public void testMoveLeft() { 33 | Node node = Node.createLeftNode(); 34 | Marker marker = node.move(new Marker(3)); 35 | assertEquals(new Marker(2), marker); 36 | } 37 | 38 | public void testMoveCenter() { 39 | Node node = Node.createCenterNode(); 40 | Marker marker = node.move(new Marker(3)); 41 | assertEquals(new Marker(3), marker); 42 | } 43 | 44 | public void testSymbol() { 45 | Node node = Node.createCenterNode(); 46 | StringBuilder sb = new StringBuilder(); 47 | node.appendSymbol(sb); 48 | assertEquals("0", sb.toString()); 49 | // node = Node.createLeftNode(); 50 | // assertEquals(-1, node.getSymbol()); 51 | // node = Node.createRightNode(); 52 | // assertEquals(1, node.getSymbol()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/ladder/PositionTest.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import junit.framework.TestCase; 5 | 6 | public class PositionTest extends TestCase { 7 | 8 | public void testCreate() { 9 | Position position = Position.create(new NaturalNumber(3), new NaturalNumber(4)); 10 | Position position2 = Position.create(3, 4); 11 | assertEquals(position, position2); 12 | } 13 | 14 | public void testCreateFromArrayIndex() { 15 | Position arrayPosition = Position.createFromArrayIndex(2, 3); 16 | Position position = Position.create(new NaturalNumber(3), new NaturalNumber(4)); 17 | assertEquals(position, arrayPosition); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/ladder/RandomNaturalNumberTest.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import java.util.List; 5 | import junit.framework.TestCase; 6 | 7 | public class RandomNaturalNumberTest extends TestCase { 8 | 9 | public void testGetPositionOfPerson() { 10 | RandomNaturalNumber number = new RandomNaturalNumber(4); 11 | int actual = number.getPositionOfPerson(3); 12 | // 1 2 3 13 | // 4 5 6 => 높이는 2, 사람의 위치는 1 14 | assertEquals(1, actual); 15 | } 16 | 17 | public void testGetPositionOfHeight() { 18 | RandomNaturalNumber number = new RandomNaturalNumber(4); 19 | int actual = number.getPositionOfHeight(3); 20 | assertEquals(2, actual); 21 | } 22 | 23 | public void testIsMultipleOfPerson() { 24 | RandomNaturalNumber randomNumber = new RandomNaturalNumber(8); 25 | assertTrue(randomNumber.isMultipleOfPerson(4)); 26 | randomNumber = new RandomNaturalNumber(5); 27 | assertFalse(randomNumber.isMultipleOfPerson(3)); 28 | } 29 | 30 | public void testRandomNumberEquals() { 31 | assertTrue(new RandomNaturalNumber(1).equals(new NaturalNumber(1))); 32 | assertTrue(new NaturalNumber(1).equals(new RandomNaturalNumber(1))); 33 | } 34 | 35 | public void testCheckedNaturalNumbers() { 36 | RandomNaturalNumber randomNumber = new RandomNaturalNumber(2); 37 | List naturalNumbers = randomNumber.checkedNaturalNumbers(new NaturalNumber(3)); 38 | assertTrue(naturalNumbers.size() == 3); 39 | } 40 | 41 | public void testCheckNaturalNumbersWheFirst() { 42 | RandomNaturalNumber randomNumber = new RandomNaturalNumber(4); 43 | List naturalNumbers = randomNumber.checkedNaturalNumbers(new NaturalNumber(3)); 44 | assertTrue(naturalNumbers.size() == 2); 45 | } 46 | } -------------------------------------------------------------------------------- /test/ladder/RowTest.java: -------------------------------------------------------------------------------- 1 | package ladder; 2 | 3 | import core.NaturalNumber; 4 | import junit.framework.TestCase; 5 | 6 | public class RowTest extends TestCase { 7 | 8 | private Row row; 9 | 10 | public void setUp() throws Exception { 11 | row = new Row(new NaturalNumber(3)); 12 | } 13 | 14 | public void testStartPositionWhenOvernoOfPersons() { 15 | try { 16 | row.drawLine(new NaturalNumber(3)); 17 | fail("IllegalArgumentException 에러가 발생해야 한다."); 18 | } catch (IllegalArgumentException e) { 19 | assertTrue(true); 20 | } 21 | } 22 | 23 | public void testDrawLineWhenAlreadyDrawingPoint() { 24 | try { 25 | row.drawLine(new NaturalNumber(1)); 26 | row.drawLine(new NaturalNumber(2)); 27 | fail("IllegalArgumentException 에러가 발생해야 한다."); 28 | } catch (IllegalArgumentException e) { 29 | assertTrue(true); 30 | } 31 | } 32 | 33 | public void testMoveWhenNoLine() { 34 | NaturalNumber target = row.move(new Marker(1)); 35 | assertEquals(1, target.getNumber()); 36 | 37 | target = row.move(new Marker(3)); 38 | assertEquals(3, target.getNumber()); 39 | } 40 | 41 | public void testMoveWhenLineLeft() { 42 | // 0 1 1 43 | row.drawLine(new NaturalNumber(2)); 44 | NaturalNumber target = row.move(new Marker(3)); 45 | assertEquals(2, target.getNumber()); 46 | } 47 | 48 | public void testMoveWhenLineRight() { 49 | // 0 1 1 50 | row.drawLine(new NaturalNumber(2)); 51 | NaturalNumber target = row.move(new Marker(2)); 52 | assertEquals(3, target.getNumber()); 53 | } 54 | } -------------------------------------------------------------------------------- /test/ladder/creator/RandomLadderCreatorTest.java: -------------------------------------------------------------------------------- 1 | package ladder.creator; 2 | 3 | import core.NaturalNumber; 4 | import java.util.List; 5 | import junit.framework.TestCase; 6 | import ladder.Position; 7 | 8 | public class RandomLadderCreatorTest extends TestCase { 9 | 10 | public void testGenerateStartPositions() { 11 | ManualLadderCreator manualLadderCreator = new ManualLadderCreator(new NaturalNumber(3), new NaturalNumber(4)); 12 | RandomLadderCreator creator = new RandomLadderCreator(manualLadderCreator); 13 | List positions = creator.generateStartPositions(); 14 | for (Position position : positions) { 15 | System.out.println(String.format("position : %s", position)); 16 | 17 | } 18 | } 19 | } 20 | --------------------------------------------------------------------------------