From d02f1bd1bee8269e9444e7dafe58321fb749d8c9 Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Wed, 5 Mar 2025 19:07:51 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=201=EB=8B=A8=EA=B3=84=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/LadderApplication.java | 10 ++ .../java/controller/LadderController.java | 33 +++++++ src/main/java/dto/LineDto.java | 29 ++++++ src/main/java/model/Ladder.java | 23 +++++ src/main/java/model/Line.java | 99 +++++++++++++++++++ src/main/java/model/Link.java | 39 ++++++++ src/main/java/model/LinkStatus.java | 18 ++++ src/main/java/view/LadderOutputView.java | 47 +++++++++ 8 files changed, 298 insertions(+) create mode 100644 src/main/java/LadderApplication.java create mode 100644 src/main/java/controller/LadderController.java create mode 100644 src/main/java/dto/LineDto.java create mode 100644 src/main/java/model/Ladder.java create mode 100644 src/main/java/model/Line.java create mode 100644 src/main/java/model/Link.java create mode 100644 src/main/java/model/LinkStatus.java create mode 100644 src/main/java/view/LadderOutputView.java diff --git a/src/main/java/LadderApplication.java b/src/main/java/LadderApplication.java new file mode 100644 index 0000000..67f73e9 --- /dev/null +++ b/src/main/java/LadderApplication.java @@ -0,0 +1,10 @@ +import controller.LadderController; + +public class LadderApplication { + public static void main(String[] args) { + LadderController ladderController = LadderController.getInstance(); + + ladderController.run(); + } + +} diff --git a/src/main/java/controller/LadderController.java b/src/main/java/controller/LadderController.java new file mode 100644 index 0000000..856f460 --- /dev/null +++ b/src/main/java/controller/LadderController.java @@ -0,0 +1,33 @@ +package controller; + +import dto.LineDto; +import model.Ladder; +import model.Line; +import view.LadderOutputView; + +import java.util.List; + +public class LadderController { + private static final LadderController ladderController = new LadderController(); + + private final LadderOutputView ladderOutputView = LadderOutputView.getInstance(); + + private LadderController() { + } + + public static LadderController getInstance() { + return ladderController; + } + + public void run() { + Ladder ladder = new Ladder(4, 4); + List lines = ladder.getLines(); + + ladderOutputView.printResultHeader(); + for (Line line : lines) { + LineDto lineDto = LineDto.from(line); + ladderOutputView.printLine(lineDto); + } + } + +} diff --git a/src/main/java/dto/LineDto.java b/src/main/java/dto/LineDto.java new file mode 100644 index 0000000..bcb17b2 --- /dev/null +++ b/src/main/java/dto/LineDto.java @@ -0,0 +1,29 @@ +package dto; + +import model.Line; +import model.Link; +import model.LinkStatus; + +import java.util.List; + +public class LineDto { + private final List linkExistCollection; + + private LineDto(List linkExistCollection) { + this.linkExistCollection = linkExistCollection; + } + + public static LineDto from(Line line) { + List linkExistCollection = line.getLinks().stream() + .map(Link::getLinkstatus) + .map(LinkStatus::isPresent) + .toList(); + + return new LineDto(linkExistCollection); + } + + public List getLinkExistCollection() { + return List.copyOf(linkExistCollection); + } + +} diff --git a/src/main/java/model/Ladder.java b/src/main/java/model/Ladder.java new file mode 100644 index 0000000..c0df4f6 --- /dev/null +++ b/src/main/java/model/Ladder.java @@ -0,0 +1,23 @@ +package model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Ladder { + private final List lines; + + public Ladder(int width, int height) { + List lines = new ArrayList<>(); + for (int i = 0; i < height; i++) { + lines.add(new Line(width)); + } + + this.lines = Collections.unmodifiableList(lines); + } + + public List getLines() { + return List.copyOf(lines); + } + +} diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java new file mode 100644 index 0000000..3945231 --- /dev/null +++ b/src/main/java/model/Line.java @@ -0,0 +1,99 @@ +package model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static model.LinkStatus.PRESENT; +import static model.LinkStatus.UNDEFINED; + +public class Line { + private static final Random random = new Random(); + + private final List links; + + public Line(int width) { + int size = width - 1; + List links = initializeLinks(size); + + convertUndefinedValuesToSuitableStatus(links); + + this.links = List.copyOf(links); + } + + public List getLinks() { + return List.copyOf(links); + } + + private List initializeLinks(int size) { + List links = new ArrayList<>(); + for (int i = 0; i < size; i++) { + links.add(Link.getUndefinedLink()); + } + + return List.copyOf(links); + } + + private void convertUndefinedValuesToSuitableStatus(List links) { + int index = getRandomStartIndex(links); + + while (containsUndefined(links)) { + boolean connectDecider = getConnectDecider(); + boolean connectable = isConnectable(index, links); + + Link link = links.get(index); + link.setLinkStatus(connectDecider, connectable); + + index = getNextIndex(index, links); + } + } + + private int getRandomStartIndex(List links) { + return random.nextInt(links.size()); + } + + private boolean containsUndefined(List links) { + return links.stream() + .anyMatch(link -> link.getLinkstatus() == UNDEFINED); + } + + private boolean getConnectDecider() { + return random.nextBoolean(); + } + + private boolean isConnectable(int index, List links) { + if (isFirstIndex(index)) { + return isRightNotPresent(index, links); + } + if (isLastIndex(index, links)) { + return isLeftNotPresent(index, links); + } + + return isRightNotPresent(index, links) && isLeftNotPresent(index, links); + } + + private boolean isFirstIndex(int index) { + return index == 0; + } + + private boolean isLastIndex(int index, List links) { + return index == links.size() - 1; + } + + private boolean isRightNotPresent(int index, List links) { + Link rightLink = links.get(index + 1); + + return rightLink.getLinkstatus() != PRESENT; + } + + private boolean isLeftNotPresent(int index, List links) { + Link leftLink = links.get(index - 1); + + return leftLink.getLinkstatus() != PRESENT; + } + + private int getNextIndex(int index, List linkStatuses) { + return ++index % linkStatuses.size(); + } + +} diff --git a/src/main/java/model/Link.java b/src/main/java/model/Link.java new file mode 100644 index 0000000..f6f3424 --- /dev/null +++ b/src/main/java/model/Link.java @@ -0,0 +1,39 @@ +package model; + +public class Link { + private LinkStatus linkstatus; + + private Link(LinkStatus linkstatus) { + this.linkstatus = linkstatus; + } + + public static Link getUndefinedLink() { + return new Link(LinkStatus.UNDEFINED); + } + + public void setLinkStatus(boolean connectDecider, boolean connectable) { + validateUpdateStatus(); + + if (connectDecider && connectable) { + this.linkstatus = LinkStatus.PRESENT; + return; + } + + this.linkstatus = LinkStatus.ABSENT; + } + + public LinkStatus getLinkstatus() { + return linkstatus; + } + + private void validateUpdateStatus() { + if (isAlreadyFixed()) { + throw new IllegalStateException("이미 값이 결정된 링크입니다"); + } + } + + private boolean isAlreadyFixed() { + return this.linkstatus != LinkStatus.UNDEFINED; + } + +} diff --git a/src/main/java/model/LinkStatus.java b/src/main/java/model/LinkStatus.java new file mode 100644 index 0000000..7c98470 --- /dev/null +++ b/src/main/java/model/LinkStatus.java @@ -0,0 +1,18 @@ +package model; + +public enum LinkStatus { + UNDEFINED(false), + ABSENT(false), + PRESENT(true); + + private final boolean present; + + LinkStatus(boolean present) { + this.present = present; + } + + public boolean isPresent() { + return this.present; + } + +} diff --git a/src/main/java/view/LadderOutputView.java b/src/main/java/view/LadderOutputView.java new file mode 100644 index 0000000..1463885 --- /dev/null +++ b/src/main/java/view/LadderOutputView.java @@ -0,0 +1,47 @@ +package view; + +import dto.LineDto; + +public class LadderOutputView { + + private static final String INDENTATION = " "; + private static final String DASH_COUPLER = "-----"; + private static final String BLANK_COUPLER = " "; + private static final String PILLAR = "|"; + + private LadderOutputView() { + } + + private static final LadderOutputView ladderOutputView = new LadderOutputView(); + + public static LadderOutputView getInstance() { + return ladderOutputView; + } + + public void printResultHeader() { + System.out.println("실행결과"); + System.out.println(); + } + + public void printLine(LineDto lineDto) { + StringBuilder output = new StringBuilder() + .append(INDENTATION) + .append(PILLAR); + + for (boolean isExist : lineDto.getLinkExistCollection()) { + String coupler = getCoupler(isExist); + output.append(coupler) + .append(PILLAR); + } + + System.out.println(output); + } + + private String getCoupler(boolean isLinkExist) { + if (isLinkExist) { + return DASH_COUPLER; + } + return BLANK_COUPLER; + } + +} From f072a93f3c23e0e4b15851b5f7fa2721e8a876ef Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Wed, 5 Mar 2025 21:47:06 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor:=20=EC=96=B4=EC=83=89=ED=95=9C=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EC=9D=98=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Line.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java index 3945231..c509641 100644 --- a/src/main/java/model/Line.java +++ b/src/main/java/model/Line.java @@ -15,8 +15,7 @@ public class Line { public Line(int width) { int size = width - 1; List links = initializeLinks(size); - - convertUndefinedValuesToSuitableStatus(links); + setupLinks(links); this.links = List.copyOf(links); } @@ -34,7 +33,7 @@ private List initializeLinks(int size) { return List.copyOf(links); } - private void convertUndefinedValuesToSuitableStatus(List links) { + private void setupLinks(List links) { int index = getRandomStartIndex(links); while (containsUndefined(links)) { From 00f6903d22fea5f121084f8e7538093958b2058f Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Wed, 5 Mar 2025 22:04:07 +0900 Subject: [PATCH 3/8] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/LinkStatus.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/model/LinkStatus.java b/src/main/java/model/LinkStatus.java index 7c98470..479bf2f 100644 --- a/src/main/java/model/LinkStatus.java +++ b/src/main/java/model/LinkStatus.java @@ -1,18 +1,10 @@ package model; public enum LinkStatus { - UNDEFINED(false), - ABSENT(false), - PRESENT(true); - - private final boolean present; - - LinkStatus(boolean present) { - this.present = present; - } + UNDEFINED, ABSENT, PRESENT; public boolean isPresent() { - return this.present; + return this == PRESENT; } } From 35edfcf0cc318949c1d1b547d6a9c061cc4b032c Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Wed, 5 Mar 2025 22:22:29 +0900 Subject: [PATCH 4/8] =?UTF-8?q?refactor:=20=EC=A0=81=EC=A0=88=ED=95=9C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Link.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/model/Link.java b/src/main/java/model/Link.java index f6f3424..cc007b2 100644 --- a/src/main/java/model/Link.java +++ b/src/main/java/model/Link.java @@ -28,7 +28,7 @@ public LinkStatus getLinkstatus() { private void validateUpdateStatus() { if (isAlreadyFixed()) { - throw new IllegalStateException("이미 값이 결정된 링크입니다"); + throw new UnsupportedOperationException("이미 값이 결정된 링크입니다"); } } From 7059223d02e82edb2e3147a16da0c0b413067030 Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Mon, 10 Mar 2025 20:18:46 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor:=20=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/LadderController.java | 5 +++- src/main/java/model/ConnectDecider.java | 26 +++++++++++++++++++ src/main/java/model/Line.java | 15 ++++------- src/main/java/model/Link.java | 23 +++++----------- src/main/java/view/LadderOutputView.java | 5 ++-- 5 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 src/main/java/model/ConnectDecider.java diff --git a/src/main/java/controller/LadderController.java b/src/main/java/controller/LadderController.java index 856f460..ae66260 100644 --- a/src/main/java/controller/LadderController.java +++ b/src/main/java/controller/LadderController.java @@ -8,6 +8,9 @@ import java.util.List; public class LadderController { + private static final int DEFAULT_LADDER_HEIGHT = 4; + private static final int DEFAULT_LADDER_WIDTH = 4; + private static final LadderController ladderController = new LadderController(); private final LadderOutputView ladderOutputView = LadderOutputView.getInstance(); @@ -20,7 +23,7 @@ public static LadderController getInstance() { } public void run() { - Ladder ladder = new Ladder(4, 4); + Ladder ladder = new Ladder(DEFAULT_LADDER_WIDTH, DEFAULT_LADDER_HEIGHT); List lines = ladder.getLines(); ladderOutputView.printResultHeader(); diff --git a/src/main/java/model/ConnectDecider.java b/src/main/java/model/ConnectDecider.java new file mode 100644 index 0000000..32a58d7 --- /dev/null +++ b/src/main/java/model/ConnectDecider.java @@ -0,0 +1,26 @@ +package model; + +import java.util.Random; + +public class ConnectDecider { + private static final Random random = new Random(); + + private final boolean canBeConnected; + + private ConnectDecider(boolean canBeConnected) { + this.canBeConnected = canBeConnected; + } + + public static ConnectDecider getRandomConnectDecider() { + return new ConnectDecider(random.nextBoolean()); + } + + public static ConnectDecider from(boolean connectDecider) { + return new ConnectDecider(connectDecider); + } + + public boolean isCanBeConnected() { + return this.canBeConnected; + } + +} diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java index c509641..70b7254 100644 --- a/src/main/java/model/Line.java +++ b/src/main/java/model/Line.java @@ -30,18 +30,17 @@ private List initializeLinks(int size) { links.add(Link.getUndefinedLink()); } - return List.copyOf(links); + return links; } private void setupLinks(List links) { int index = getRandomStartIndex(links); while (containsUndefined(links)) { - boolean connectDecider = getConnectDecider(); + ConnectDecider connectDecider = ConnectDecider.getRandomConnectDecider(); boolean connectable = isConnectable(index, links); - Link link = links.get(index); - link.setLinkStatus(connectDecider, connectable); + links.set(index, Link.getDefinedLink(connectDecider, connectable)); index = getNextIndex(index, links); } @@ -53,11 +52,7 @@ private int getRandomStartIndex(List links) { private boolean containsUndefined(List links) { return links.stream() - .anyMatch(link -> link.getLinkstatus() == UNDEFINED); - } - - private boolean getConnectDecider() { - return random.nextBoolean(); + .anyMatch(Link::isUndefined); } private boolean isConnectable(int index, List links) { @@ -92,7 +87,7 @@ private boolean isLeftNotPresent(int index, List links) { } private int getNextIndex(int index, List linkStatuses) { - return ++index % linkStatuses.size(); + return (index + 1) % linkStatuses.size(); } } diff --git a/src/main/java/model/Link.java b/src/main/java/model/Link.java index cc007b2..2db58d1 100644 --- a/src/main/java/model/Link.java +++ b/src/main/java/model/Link.java @@ -1,7 +1,7 @@ package model; public class Link { - private LinkStatus linkstatus; + private final LinkStatus linkstatus; private Link(LinkStatus linkstatus) { this.linkstatus = linkstatus; @@ -11,29 +11,20 @@ public static Link getUndefinedLink() { return new Link(LinkStatus.UNDEFINED); } - public void setLinkStatus(boolean connectDecider, boolean connectable) { - validateUpdateStatus(); - - if (connectDecider && connectable) { - this.linkstatus = LinkStatus.PRESENT; - return; + public static Link getDefinedLink(ConnectDecider connectDecider, boolean connectable) { + if (connectDecider.isCanBeConnected() && connectable) { + return new Link(LinkStatus.PRESENT); } - this.linkstatus = LinkStatus.ABSENT; + return new Link(LinkStatus.ABSENT); } public LinkStatus getLinkstatus() { return linkstatus; } - private void validateUpdateStatus() { - if (isAlreadyFixed()) { - throw new UnsupportedOperationException("이미 값이 결정된 링크입니다"); - } - } - - private boolean isAlreadyFixed() { - return this.linkstatus != LinkStatus.UNDEFINED; + public boolean isUndefined() { + return this.linkstatus == LinkStatus.UNDEFINED; } } diff --git a/src/main/java/view/LadderOutputView.java b/src/main/java/view/LadderOutputView.java index 1463885..15f870a 100644 --- a/src/main/java/view/LadderOutputView.java +++ b/src/main/java/view/LadderOutputView.java @@ -3,17 +3,16 @@ import dto.LineDto; public class LadderOutputView { - private static final String INDENTATION = " "; private static final String DASH_COUPLER = "-----"; private static final String BLANK_COUPLER = " "; private static final String PILLAR = "|"; + private static final LadderOutputView ladderOutputView = new LadderOutputView(); + private LadderOutputView() { } - private static final LadderOutputView ladderOutputView = new LadderOutputView(); - public static LadderOutputView getInstance() { return ladderOutputView; } From 2fde758780296081d23f0963beec6bbc515c9574 Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Mon, 10 Mar 2025 20:28:40 +0900 Subject: [PATCH 6/8] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=B6=88=EB=B3=80=20=EC=BB=AC=EB=A0=89=EC=85=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/Ladder.java | 2 +- src/main/java/model/Line.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/model/Ladder.java b/src/main/java/model/Ladder.java index c0df4f6..4d4daea 100644 --- a/src/main/java/model/Ladder.java +++ b/src/main/java/model/Ladder.java @@ -17,7 +17,7 @@ public Ladder(int width, int height) { } public List getLines() { - return List.copyOf(lines); + return lines; } } diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java index 70b7254..579e160 100644 --- a/src/main/java/model/Line.java +++ b/src/main/java/model/Line.java @@ -1,6 +1,7 @@ package model; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; @@ -21,7 +22,7 @@ public Line(int width) { } public List getLinks() { - return List.copyOf(links); + return links; } private List initializeLinks(int size) { From 63d9750998f51dc71912cb158d48a68f7004c0c1 Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Fri, 14 Mar 2025 13:20:57 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat:=202=20~=205=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/LadderApplication.java | 10 +- .../java/controller/LadderController.java | 88 +++++++++++++++-- src/main/java/dto/LadderResultDto.java | 4 + src/main/java/dto/LineDto.java | 10 +- src/main/java/model/ConnectDecider.java | 26 ----- .../model/DetachedRandomLinksGenerator.java | 98 +++++++++++++++++++ src/main/java/model/Ladder.java | 41 +++++++- .../java/model/LadderResultCalculator.java | 37 +++++++ src/main/java/model/LadderUser.java | 56 +++++++++++ src/main/java/model/LadderUsers.java | 56 +++++++++++ src/main/java/model/Line.java | 81 +++++---------- src/main/java/model/Link.java | 5 +- src/main/java/model/LinkStatus.java | 5 +- src/main/java/model/LinksGenerator.java | 9 ++ src/main/java/view/LadderInputView.java | 56 +++++++++++ src/main/java/view/LadderOutputView.java | 87 +++++++++++++++- .../fixture/LadderResultValuesFixture.java | 20 ++++ src/test/java/fixture/LadderUserFixture.java | 25 +++++ src/test/java/fixture/LadderUsersFixture.java | 25 +++++ .../model/LadderResultCalculatorTest.java | 27 +++++ src/test/java/model/LadderTest.java | 50 ++++++++++ src/test/java/model/LadderUserTest.java | 56 +++++++++++ src/test/java/model/LadderUsersTest.java | 70 +++++++++++++ src/test/java/model/LineTest.java | 23 +++++ src/test/java/model/LinkTest.java | 72 ++++++++++++++ 25 files changed, 926 insertions(+), 111 deletions(-) create mode 100644 src/main/java/dto/LadderResultDto.java delete mode 100644 src/main/java/model/ConnectDecider.java create mode 100644 src/main/java/model/DetachedRandomLinksGenerator.java create mode 100644 src/main/java/model/LadderResultCalculator.java create mode 100644 src/main/java/model/LadderUser.java create mode 100644 src/main/java/model/LadderUsers.java create mode 100644 src/main/java/model/LinksGenerator.java create mode 100644 src/main/java/view/LadderInputView.java create mode 100644 src/test/java/fixture/LadderResultValuesFixture.java create mode 100644 src/test/java/fixture/LadderUserFixture.java create mode 100644 src/test/java/fixture/LadderUsersFixture.java create mode 100644 src/test/java/model/LadderResultCalculatorTest.java create mode 100644 src/test/java/model/LadderTest.java create mode 100644 src/test/java/model/LadderUserTest.java create mode 100644 src/test/java/model/LadderUsersTest.java create mode 100644 src/test/java/model/LineTest.java create mode 100644 src/test/java/model/LinkTest.java diff --git a/src/main/java/LadderApplication.java b/src/main/java/LadderApplication.java index 67f73e9..e868d03 100644 --- a/src/main/java/LadderApplication.java +++ b/src/main/java/LadderApplication.java @@ -1,10 +1,14 @@ import controller.LadderController; public class LadderApplication { - public static void main(String[] args) { - LadderController ladderController = LadderController.getInstance(); - ladderController.run(); + public static void main(String[] args) { + try { + LadderController ladderController = LadderController.getInstance(); + ladderController.run(); + } catch (Exception e) { + e.printStackTrace(); + } } } diff --git a/src/main/java/controller/LadderController.java b/src/main/java/controller/LadderController.java index ae66260..43a15e3 100644 --- a/src/main/java/controller/LadderController.java +++ b/src/main/java/controller/LadderController.java @@ -1,19 +1,23 @@ package controller; +import dto.LadderResultDto; import dto.LineDto; -import model.Ladder; -import model.Line; +import model.*; +import view.LadderInputView; import view.LadderOutputView; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class LadderController { - private static final int DEFAULT_LADDER_HEIGHT = 4; - private static final int DEFAULT_LADDER_WIDTH = 4; + + private static final String PRINT_EVERY_RESULT = "all"; private static final LadderController ladderController = new LadderController(); - private final LadderOutputView ladderOutputView = LadderOutputView.getInstance(); + private static final LadderOutputView ladderOutputView = LadderOutputView.getInstance(); + private static final LadderInputView ladderInputView = LadderInputView.getInstance(); private LadderController() { } @@ -23,14 +27,82 @@ public static LadderController getInstance() { } public void run() { - Ladder ladder = new Ladder(DEFAULT_LADDER_WIDTH, DEFAULT_LADDER_HEIGHT); - List lines = ladder.getLines(); + LadderUsers ladderUsers = getLadderUsers(); + String[] resultValues = ladderInputView.getResultValues(); + int height = ladderInputView.getHeight(); + + Ladder ladder = new Ladder(ladderUsers.size(), height); + LadderResultCalculator ladderResultCalculator = new LadderResultCalculator(ladderUsers, resultValues); + printLadderShape(ladder, ladderUsers, resultValues); + + printLadderResultUntilPrintEveryResult(ladder, ladderResultCalculator, ladderUsers); + } + + private LadderUsers getLadderUsers() { + String[] names = ladderInputView.getNames(); + + List ladderUsers = Arrays.stream(names) + .map(LadderUser::new) + .toList(); - ladderOutputView.printResultHeader(); + return new LadderUsers(ladderUsers); + } + + private void printLadderShape(Ladder ladder, LadderUsers ladderUsers, String[] resultValues) { + ladderOutputView.printLadderResultHeader(); + ladderOutputView.printNames(ladderUsers.getNames()); + + List lines = ladder.getLines(); for (Line line : lines) { LineDto lineDto = LineDto.from(line); ladderOutputView.printLine(lineDto); } + + ladderOutputView.printResultValues(resultValues); + } + + private void printLadderResultUntilPrintEveryResult( + Ladder ladder, + LadderResultCalculator ladderResultCalculator, + LadderUsers ladderUsers + ) { + String targetName = ladderInputView.getTargetName(); + + while (shouldPrintSingleResult(targetName)) { + printSingleLadderResult(targetName, ladder, ladderResultCalculator); + targetName = ladderInputView.getTargetName(); + } + + printEveryLadderResult(ladder, ladderResultCalculator, ladderUsers); + } + + private boolean shouldPrintSingleResult(String targetName) { + return !targetName.equals(PRINT_EVERY_RESULT); + } + + private void printSingleLadderResult( + String targetName, + Ladder ladder, + LadderResultCalculator ladderResultCalculator + ) { + String result = ladderResultCalculator.calculate(targetName, ladder); + + ladderOutputView.printLadderResult(result); + } + + private void printEveryLadderResult( + Ladder ladder, + LadderResultCalculator ladderResultCalculator, + LadderUsers ladderUsers + ) { + List ladderResultDtos = new ArrayList<>(); + + for (String name : ladderUsers.getNames()) { + String result = ladderResultCalculator.calculate(name, ladder); + ladderResultDtos.add(new LadderResultDto(name, result)); + } + + ladderOutputView.printLadderResults(ladderResultDtos); } } diff --git a/src/main/java/dto/LadderResultDto.java b/src/main/java/dto/LadderResultDto.java new file mode 100644 index 0000000..3bb8ed8 --- /dev/null +++ b/src/main/java/dto/LadderResultDto.java @@ -0,0 +1,4 @@ +package dto; + +public record LadderResultDto(String name, String resultValue) { +} diff --git a/src/main/java/dto/LineDto.java b/src/main/java/dto/LineDto.java index bcb17b2..323fc95 100644 --- a/src/main/java/dto/LineDto.java +++ b/src/main/java/dto/LineDto.java @@ -1,12 +1,13 @@ package dto; import model.Line; -import model.Link; -import model.LinkStatus; import java.util.List; +import static model.LinkStatus.PRESENT; + public class LineDto { + private final List linkExistCollection; private LineDto(List linkExistCollection) { @@ -15,15 +16,14 @@ private LineDto(List linkExistCollection) { public static LineDto from(Line line) { List linkExistCollection = line.getLinks().stream() - .map(Link::getLinkstatus) - .map(LinkStatus::isPresent) + .map(link -> link.getLinkstatus() == PRESENT) .toList(); return new LineDto(linkExistCollection); } public List getLinkExistCollection() { - return List.copyOf(linkExistCollection); + return linkExistCollection; } } diff --git a/src/main/java/model/ConnectDecider.java b/src/main/java/model/ConnectDecider.java deleted file mode 100644 index 32a58d7..0000000 --- a/src/main/java/model/ConnectDecider.java +++ /dev/null @@ -1,26 +0,0 @@ -package model; - -import java.util.Random; - -public class ConnectDecider { - private static final Random random = new Random(); - - private final boolean canBeConnected; - - private ConnectDecider(boolean canBeConnected) { - this.canBeConnected = canBeConnected; - } - - public static ConnectDecider getRandomConnectDecider() { - return new ConnectDecider(random.nextBoolean()); - } - - public static ConnectDecider from(boolean connectDecider) { - return new ConnectDecider(connectDecider); - } - - public boolean isCanBeConnected() { - return this.canBeConnected; - } - -} diff --git a/src/main/java/model/DetachedRandomLinksGenerator.java b/src/main/java/model/DetachedRandomLinksGenerator.java new file mode 100644 index 0000000..e10e307 --- /dev/null +++ b/src/main/java/model/DetachedRandomLinksGenerator.java @@ -0,0 +1,98 @@ +package model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import static model.LinkStatus.PRESENT; + +public class DetachedRandomLinksGenerator implements LinksGenerator { + + private static final Random random = new Random(); + + private final int linksSize; + + public DetachedRandomLinksGenerator(int linksSize) { + this.linksSize = linksSize; + } + + public List generate() { + List links = getUndefinedLinks(linksSize); + resolveUndefinedLinks(links); + + return Collections.unmodifiableList(links); + } + + private List getUndefinedLinks(int linksSize) { + List links = new ArrayList<>(); + for (int i = 0; i < linksSize; i++) { + links.add(Link.getUndefinedLink()); + } + + return links; + } + + private void resolveUndefinedLinks(List links) { + int index = getRandomStartIndex(links); + + while (containsUndefined(links)) { + boolean connectDecider = getConnectDecider(); + boolean connectable = isConnectable(index, links); + + links.set(index, Link.getDefinedLink(connectDecider, connectable)); + + index = getNextIndex(index, links); + } + } + + private int getRandomStartIndex(List links) { + return random.nextInt(links.size()); + } + + private boolean containsUndefined(List links) { + return links.stream() + .anyMatch(Link::isUndefined); + } + + private boolean getConnectDecider() { + return random.nextBoolean(); + } + + private boolean isConnectable(int index, List links) { + return isLeftNotPresent(index, links) && isRightNotPresent(index, links); + } + + private boolean isLeftNotPresent(int index, List links) { + if (isFirstIndex(index)) { + return true; + } + + Link leftLink = links.get(index - 1); + + return leftLink.getLinkstatus() != PRESENT; + } + + private boolean isRightNotPresent(int index, List links) { + if (isLastIndex(index, links)) { + return true; + } + + Link rightLink = links.get(index + 1); + + return rightLink.getLinkstatus() != PRESENT; + } + + private boolean isFirstIndex(int index) { + return index == 0; + } + + private boolean isLastIndex(int index, List links) { + return index == links.size() - 1; + } + + private int getNextIndex(int index, List linkStatuses) { + return (index + 1) % linkStatuses.size(); + } + +} diff --git a/src/main/java/model/Ladder.java b/src/main/java/model/Ladder.java index 4d4daea..fcb49be 100644 --- a/src/main/java/model/Ladder.java +++ b/src/main/java/model/Ladder.java @@ -5,19 +5,52 @@ import java.util.List; public class Ladder { + + private static final int MINIMUM_WIDTH = 2; + private static final int MINIMUM_HEIGHT = 1; + private final List lines; public Ladder(int width, int height) { + validateSize(width, height); + + this.lines = createLines(width, height); + } + + public List getLines() { + return lines; + } + + public int getEndPoint(int startPoint) { + int point = startPoint; + + for (Line line : lines) { + point = line.getNextPoint(point); + } + + return point; + } + + private List createLines(int width, int height) { + int linksSize = width - 1; + DetachedRandomLinksGenerator linksGenerator = new DetachedRandomLinksGenerator(linksSize); + List lines = new ArrayList<>(); for (int i = 0; i < height; i++) { - lines.add(new Line(width)); + Line line = new Line(linksGenerator); + lines.add(line); } - this.lines = Collections.unmodifiableList(lines); + return Collections.unmodifiableList(lines); } - public List getLines() { - return lines; + private void validateSize(int width, int height) { + if (width < MINIMUM_WIDTH) { + throw new IllegalArgumentException("사다리의 너비는 " + MINIMUM_WIDTH + "보다 짧을 수 없습니다. 전달된 값: " + width); + } + if (height < MINIMUM_HEIGHT) { + throw new IllegalArgumentException("사다리의 높이는 " + MINIMUM_HEIGHT + "보다 짧을 수 없습니다. 전달된 값: " + height); + } } } diff --git a/src/main/java/model/LadderResultCalculator.java b/src/main/java/model/LadderResultCalculator.java new file mode 100644 index 0000000..08c177a --- /dev/null +++ b/src/main/java/model/LadderResultCalculator.java @@ -0,0 +1,37 @@ +package model; + +import java.util.Arrays; +import java.util.List; + +public class LadderResultCalculator { + + private final LadderUsers ladderUsers; + private final List resultValues; + + public LadderResultCalculator(LadderUsers ladderUsers, String[] resultValues) { + validateSize(ladderUsers, resultValues); + + this.ladderUsers = ladderUsers; + this.resultValues = Arrays.stream(resultValues) + .toList(); + } + + public String calculate(String name, Ladder ladder) { + int index = ladderUsers.findIndexOfUserByName(name) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 이름입니다.")); + int endPoint = ladder.getEndPoint(index); + + return findResultByIndex(endPoint); + } + + private void validateSize(LadderUsers ladderUsers, String[] resultValues) { + if (ladderUsers.size() != resultValues.length) { + throw new IllegalArgumentException("참여자의 수와 실행 결과의 수가 일치하지 않습니다."); + } + } + + private String findResultByIndex(int index) { + return resultValues.get(index); + } + +} diff --git a/src/main/java/model/LadderUser.java b/src/main/java/model/LadderUser.java new file mode 100644 index 0000000..0bd9048 --- /dev/null +++ b/src/main/java/model/LadderUser.java @@ -0,0 +1,56 @@ +package model; + +import java.util.Objects; +import java.util.Set; + +public class LadderUser { + + private static final Set RESERVED_KEYWORD = Set.of("all"); + private static final int MAX_NAME_LENGTH = 5; + + private final String name; + + public LadderUser(String name) { + validateName(name); + + this.name = name; + } + + public String getName() { + return name; + } + + private void validateName(String name) { + if (name.trim().isEmpty()) { + throw new IllegalArgumentException("이름이 비어 있습니다."); + } + if (isReservedKeyword(name)) { + throw new IllegalArgumentException(name + "은 이름으로 사용할 수 없습니다. 사용 불가능한 이름 목록: " + RESERVED_KEYWORD); + } + if (isLongerThanMaxLength(name)) { + throw new IllegalArgumentException("이름은 " + MAX_NAME_LENGTH + "글자를 넘길 수 없습니다."); + } + } + + private boolean isReservedKeyword(String name) { + return RESERVED_KEYWORD.contains(name); + } + + private boolean isLongerThanMaxLength(String name) { + return name.length() > MAX_NAME_LENGTH; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LadderUser that = (LadderUser) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + +} diff --git a/src/main/java/model/LadderUsers.java b/src/main/java/model/LadderUsers.java new file mode 100644 index 0000000..62567cc --- /dev/null +++ b/src/main/java/model/LadderUsers.java @@ -0,0 +1,56 @@ +package model; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class LadderUsers { + + private final List ladderUsers; + + public LadderUsers(List ladderUsers) { + validateSize(ladderUsers); + validateDuplicate(ladderUsers); + + this.ladderUsers = Collections.unmodifiableList(ladderUsers); + } + + public List getNames() { + return ladderUsers.stream() + .map(LadderUser::getName) + .toList(); + } + + public Optional findIndexOfUserByName(String name) { + Optional findUser = ladderUsers.stream() + .filter(ladderUser -> ladderUser.getName().equals(name)) + .findAny(); + + return findUser.map(ladderUsers::indexOf); + } + + public int size() { + return ladderUsers.size(); + } + + private void validateSize(List ladderUsers) { + if (ladderUsers.size() < 2) { + throw new IllegalArgumentException("참여자는 2명 미만일 수 없습니다."); + } + } + + private void validateDuplicate(List ladderUsers) { + boolean duplicated = ladderUsers.stream() + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) + .values() + .stream() + .anyMatch(count -> count > 1); + + if (duplicated) { + throw new IllegalArgumentException("이름은 중복될 수 없습니다."); + } + } + +} diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java index 579e160..1068296 100644 --- a/src/main/java/model/Line.java +++ b/src/main/java/model/Line.java @@ -1,94 +1,67 @@ package model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Random; import static model.LinkStatus.PRESENT; -import static model.LinkStatus.UNDEFINED; public class Line { - private static final Random random = new Random(); private final List links; - public Line(int width) { - int size = width - 1; - List links = initializeLinks(size); - setupLinks(links); - - this.links = List.copyOf(links); + public Line(LinksGenerator linksGenerator) { + this.links = Collections.unmodifiableList(linksGenerator.generate()); } public List getLinks() { return links; } - private List initializeLinks(int size) { - List links = new ArrayList<>(); - for (int i = 0; i < size; i++) { - links.add(Link.getUndefinedLink()); + public int getNextPoint(int point) { + if (shouldMoveToLeft(point)) { + return point - 1; + } + if (shouldMoveToRight(point)) { + return point + 1; } - return links; + return point; } - private void setupLinks(List links) { - int index = getRandomStartIndex(links); - - while (containsUndefined(links)) { - ConnectDecider connectDecider = ConnectDecider.getRandomConnectDecider(); - boolean connectable = isConnectable(index, links); - - links.set(index, Link.getDefinedLink(connectDecider, connectable)); - - index = getNextIndex(index, links); + private boolean shouldMoveToLeft(int point) { + if (isFirstPoint(point)) { + return false; } - } - private int getRandomStartIndex(List links) { - return random.nextInt(links.size()); + return isLeftLinkPresent(point); } - private boolean containsUndefined(List links) { - return links.stream() - .anyMatch(Link::isUndefined); - } - - private boolean isConnectable(int index, List links) { - if (isFirstIndex(index)) { - return isRightNotPresent(index, links); - } - if (isLastIndex(index, links)) { - return isLeftNotPresent(index, links); + private boolean shouldMoveToRight(int point) { + if (isLastPoint(point)) { + return false; } - return isRightNotPresent(index, links) && isLeftNotPresent(index, links); + return isRightLinkPresent(point); } - private boolean isFirstIndex(int index) { - return index == 0; + private boolean isFirstPoint(int point) { + return point == 0; } - private boolean isLastIndex(int index, List links) { - return index == links.size() - 1; + private boolean isLastPoint(int point) { + return point == links.size(); } - private boolean isRightNotPresent(int index, List links) { - Link rightLink = links.get(index + 1); - - return rightLink.getLinkstatus() != PRESENT; - } - - private boolean isLeftNotPresent(int index, List links) { + private boolean isLeftLinkPresent(int index) { Link leftLink = links.get(index - 1); - return leftLink.getLinkstatus() != PRESENT; + return leftLink.getLinkstatus() == PRESENT; } - private int getNextIndex(int index, List linkStatuses) { - return (index + 1) % linkStatuses.size(); + private boolean isRightLinkPresent(int index) { + Link rightLink = links.get(index); + + return rightLink.getLinkstatus() == PRESENT; } } diff --git a/src/main/java/model/Link.java b/src/main/java/model/Link.java index 2db58d1..61fdc1d 100644 --- a/src/main/java/model/Link.java +++ b/src/main/java/model/Link.java @@ -1,6 +1,7 @@ package model; public class Link { + private final LinkStatus linkstatus; private Link(LinkStatus linkstatus) { @@ -11,8 +12,8 @@ public static Link getUndefinedLink() { return new Link(LinkStatus.UNDEFINED); } - public static Link getDefinedLink(ConnectDecider connectDecider, boolean connectable) { - if (connectDecider.isCanBeConnected() && connectable) { + public static Link getDefinedLink(boolean connectDecider, boolean connectable) { + if (connectDecider && connectable) { return new Link(LinkStatus.PRESENT); } diff --git a/src/main/java/model/LinkStatus.java b/src/main/java/model/LinkStatus.java index 479bf2f..9ed062b 100644 --- a/src/main/java/model/LinkStatus.java +++ b/src/main/java/model/LinkStatus.java @@ -1,10 +1,7 @@ package model; public enum LinkStatus { - UNDEFINED, ABSENT, PRESENT; - public boolean isPresent() { - return this == PRESENT; - } + UNDEFINED, ABSENT, PRESENT; } diff --git a/src/main/java/model/LinksGenerator.java b/src/main/java/model/LinksGenerator.java new file mode 100644 index 0000000..6b85e93 --- /dev/null +++ b/src/main/java/model/LinksGenerator.java @@ -0,0 +1,9 @@ +package model; + +import java.util.List; + +public interface LinksGenerator { + + List generate(); + +} diff --git a/src/main/java/view/LadderInputView.java b/src/main/java/view/LadderInputView.java new file mode 100644 index 0000000..1ff8387 --- /dev/null +++ b/src/main/java/view/LadderInputView.java @@ -0,0 +1,56 @@ +package view; + +import java.util.Scanner; + +public class LadderInputView { + + private static final String INPUT_SEPARATOR = ","; + + private static final LadderInputView ladderInputView = new LadderInputView(); + + private final Scanner scanner = new Scanner(System.in); + + private LadderInputView() { + } + + public static LadderInputView getInstance() { + return ladderInputView; + } + + public String[] getNames() { + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + String names = scanner.nextLine(); + + System.out.println(); + + return names.split(INPUT_SEPARATOR); + } + + public String[] getResultValues() { + System.out.println("실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"); + String resultValues = scanner.nextLine(); + + System.out.println(); + + return resultValues.split(INPUT_SEPARATOR); + } + + public int getHeight() { + System.out.println("최대 사다리 높이는 몇 개인가요?"); + int height = Integer.parseInt(scanner.nextLine()); + + System.out.println(); + + return height; + } + + public String getTargetName() { + System.out.println("결과를 보고 싶은 사람은?"); + String targetName = scanner.nextLine(); + + System.out.println(); + + return targetName; + } + +} diff --git a/src/main/java/view/LadderOutputView.java b/src/main/java/view/LadderOutputView.java index 15f870a..21fba3c 100644 --- a/src/main/java/view/LadderOutputView.java +++ b/src/main/java/view/LadderOutputView.java @@ -1,12 +1,19 @@ package view; +import dto.LadderResultDto; import dto.LineDto; +import java.util.List; + public class LadderOutputView { - private static final String INDENTATION = " "; + + private static final String LADDER_INDENTATION = " "; + private static final String VALUE_INDENTATION = " "; private static final String DASH_COUPLER = "-----"; private static final String BLANK_COUPLER = " "; private static final String PILLAR = "|"; + private static final int MAX_FORMATTED_VALUE_LENGTH = 5; + private static final String WHITE_SPACE = " "; private static final LadderOutputView ladderOutputView = new LadderOutputView(); @@ -17,14 +24,24 @@ public static LadderOutputView getInstance() { return ladderOutputView; } - public void printResultHeader() { - System.out.println("실행결과"); + public void printLadderResultHeader() { + System.out.println("사다리 결과"); System.out.println(); } + public void printNames(List names) { + StringBuilder output = new StringBuilder(VALUE_INDENTATION); + + for (String name : names) { + output.append(formatValue(name)) + .append(WHITE_SPACE); + } + + System.out.println(output); + } + public void printLine(LineDto lineDto) { - StringBuilder output = new StringBuilder() - .append(INDENTATION) + StringBuilder output = new StringBuilder(LADDER_INDENTATION) .append(PILLAR); for (boolean isExist : lineDto.getLinkExistCollection()) { @@ -36,6 +53,37 @@ public void printLine(LineDto lineDto) { System.out.println(output); } + public void printResultValues(String[] resultValues) { + StringBuilder output = new StringBuilder(VALUE_INDENTATION); + + for (String resultValue : resultValues) { + output.append(formatValue(resultValue)) + .append(WHITE_SPACE); + } + + System.out.println(output); + System.out.println(); + } + + public void printLadderResult(String resultValue) { + System.out.println("실행 결과"); + + System.out.println(resultValue); + + System.out.println(); + } + + public void printLadderResults(List ladderResultDtos) { + System.out.println("실행 결과"); + + for (LadderResultDto ladderResultDto : ladderResultDtos) { + String name = ladderResultDto.name(); + String resultValue = ladderResultDto.resultValue(); + + System.out.println(name + " : " + resultValue); + } + } + private String getCoupler(boolean isLinkExist) { if (isLinkExist) { return DASH_COUPLER; @@ -43,4 +91,33 @@ private String getCoupler(boolean isLinkExist) { return BLANK_COUPLER; } + private String formatValue(String value) { + StringBuilder valueFormatter = new StringBuilder(value); + + boolean shouldPrepend = true; + while (isShortThanMaxLength(valueFormatter)) { + appendWhiteSpace(valueFormatter, shouldPrepend); + shouldPrepend = toggle(shouldPrepend); + } + + return valueFormatter.toString(); + } + + private boolean isShortThanMaxLength(StringBuilder value) { + return value.length() < MAX_FORMATTED_VALUE_LENGTH; + } + + private void appendWhiteSpace(StringBuilder stringBuilder, boolean shouldPrepend) { + if (shouldPrepend) { + stringBuilder.insert(0, WHITE_SPACE); + return; + } + + stringBuilder.append(WHITE_SPACE); + } + + private boolean toggle(boolean value) { + return !value; + } + } diff --git a/src/test/java/fixture/LadderResultValuesFixture.java b/src/test/java/fixture/LadderResultValuesFixture.java new file mode 100644 index 0000000..40e2133 --- /dev/null +++ b/src/test/java/fixture/LadderResultValuesFixture.java @@ -0,0 +1,20 @@ +package fixture; + +import java.util.Arrays; + +public enum LadderResultValuesFixture { + + FOUR_RESULTS(new String[]{"result1", "result2", "result3", "result4"}), + FIVE_RESULTS(new String[]{"resultA", "resultB", "resultC", "resultD", "resultE"}); + + private final String[] resultValues; + + LadderResultValuesFixture(String[] resultValues) { + this.resultValues = resultValues; + } + + public String[] getValue() { + return Arrays.copyOf(resultValues, resultValues.length); + } + +} diff --git a/src/test/java/fixture/LadderUserFixture.java b/src/test/java/fixture/LadderUserFixture.java new file mode 100644 index 0000000..d4fdc47 --- /dev/null +++ b/src/test/java/fixture/LadderUserFixture.java @@ -0,0 +1,25 @@ +package fixture; + +import model.LadderUser; + +import java.util.List; + +public enum LadderUserFixture { + + FIVE_USER_COLLECTION(List.of("1", "2", "3", "4", "5")), + NOT_DUPLICATED_USER_COLLECTION(List.of("A", "B", "C", "D", "E")), + DUPLICATED_USER_COLLECTION(List.of("A", "B", "C", "A", "D")); + + private final List ladderUserCollection; + + LadderUserFixture(List names) { + this.ladderUserCollection = names.stream() + .map(LadderUser::new) + .toList(); + } + + public List getValueCollection() { + return ladderUserCollection; + } + +} diff --git a/src/test/java/fixture/LadderUsersFixture.java b/src/test/java/fixture/LadderUsersFixture.java new file mode 100644 index 0000000..9ee2d95 --- /dev/null +++ b/src/test/java/fixture/LadderUsersFixture.java @@ -0,0 +1,25 @@ +package fixture; + +import model.LadderUser; +import model.LadderUsers; + +import java.util.List; + +public enum LadderUsersFixture { + + FOUR_USERS(List.of("user1", "user2", "user3", "user4")), + FIVE_USERS(List.of("userA", "userB", "userC", "userD", "userE")); + + private final List ladderUsers; + + LadderUsersFixture(List userNames) { + this.ladderUsers = userNames.stream() + .map(LadderUser::new) + .toList(); + } + + public LadderUsers getValue() { + return new LadderUsers(ladderUsers); + } + +} diff --git a/src/test/java/model/LadderResultCalculatorTest.java b/src/test/java/model/LadderResultCalculatorTest.java new file mode 100644 index 0000000..d0dda5c --- /dev/null +++ b/src/test/java/model/LadderResultCalculatorTest.java @@ -0,0 +1,27 @@ +package model; + +import fixture.LadderResultValuesFixture; +import fixture.LadderUsersFixture; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LadderResultCalculatorTest { + + private static final LadderUsers FOUR_USERS = LadderUsersFixture.FOUR_USERS.getValue(); + private static final LadderUsers FIVE_USERS = LadderUsersFixture.FIVE_USERS.getValue(); + private static final String[] FOUR_RESULTS = LadderResultValuesFixture.FOUR_RESULTS.getValue(); + private static final String[] FIVE_RESULTS = LadderResultValuesFixture.FIVE_RESULTS.getValue(); + + @Test + @DisplayName("사용자 수와 결과 수가 다르면 예외가 발생한다") + void ifUserAmountDifferentFromResultValuesAmountThanThrowException() { + assertThatThrownBy(() -> new LadderResultCalculator(FOUR_USERS, FIVE_RESULTS)) + .isInstanceOf(IllegalArgumentException.class); + + assertThatThrownBy(() -> new LadderResultCalculator(FIVE_USERS, FOUR_RESULTS)) + .isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/model/LadderTest.java b/src/test/java/model/LadderTest.java new file mode 100644 index 0000000..0e30806 --- /dev/null +++ b/src/test/java/model/LadderTest.java @@ -0,0 +1,50 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class LadderTest { + + private static final int DEFAULT_WIDTH = 5; + private static final int DEFAULT_HEIGHT = 5; + + @Test + @DisplayName("너비와 높이를 받아 인스턴스를 생성한다") + void createByWidthAndHeight() { + assertThatCode(() -> new Ladder(DEFAULT_WIDTH, DEFAULT_HEIGHT)) + .doesNotThrowAnyException(); + } + + @ParameterizedTest + @DisplayName("너비가 너무 짧다면 예외가 발생한다") + @ValueSource(ints = {Integer.MIN_VALUE, -1, 0, 1}) + void ifWidthTooShortThenThrowException(int illegalWidth) { + assertThatThrownBy(() -> new Ladder(illegalWidth, DEFAULT_HEIGHT)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @DisplayName("높이가 너무 짧다면 예외가 발생한다") + @ValueSource(ints = {Integer.MIN_VALUE, -1, 0}) + void ifHeightTooShortThenThrowException(int illegalHeight) { + assertThatThrownBy(() -> new Ladder(DEFAULT_WIDTH, illegalHeight)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @DisplayName("생성자에 전달한 높이와 같은 수의 원소를 지닌 Line 컬렉션을 반환한다") + @ValueSource(ints = {5, 10, 200, 3000}) + void returnLineCollectionWithSameNumberOfElementAsHeight(int height) { + Ladder ladder = new Ladder(DEFAULT_WIDTH, height); + List lines = ladder.getLines(); + + assertThat(lines.size()).isEqualTo(height); + } + +} diff --git a/src/test/java/model/LadderUserTest.java b/src/test/java/model/LadderUserTest.java new file mode 100644 index 0000000..958c98c --- /dev/null +++ b/src/test/java/model/LadderUserTest.java @@ -0,0 +1,56 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LadderUserTest { + + private static final List ILLEGAL_NAMES = List.of("all"); + + @ParameterizedTest + @DisplayName("인스턴스를 생성할 때 전달된 name을 반환한다") + @ValueSource(strings = {"A", "B", "C", "D", "E"}) + void getName(String name) { + LadderUser ladderUser = new LadderUser(name); + String actualResult = ladderUser.getName(); + + assertThat(actualResult).isEqualTo(name); + } + + @Test + @DisplayName("사용할 수 없는 이름을 전달하면 예외가 발생한다") + void ifUseIllegalNameThenThrowException() { + for (String illegalName : ILLEGAL_NAMES) { + assertThatThrownBy(() -> new LadderUser(illegalName)) + .isInstanceOf(IllegalArgumentException.class); + } + } + + @ParameterizedTest + @DisplayName("너무 긴 이름을 전달하면 예외가 발생한다") + @ValueSource(strings = { + "123456", + "1234567", + "abcdefgh" + }) + void ifUseTooLongNameThenThrowException(String illegalName) { + assertThatThrownBy(() -> new LadderUser(illegalName)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @DisplayName("빈 이름을 전달하면 예외가 발생한다") + @ValueSource(strings = {"", " ", " "}) + void ifUseEmptyNameThenThrowException(String emptyName) { + assertThatThrownBy(() -> new LadderUser(emptyName)) + .isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/model/LadderUsersTest.java b/src/test/java/model/LadderUsersTest.java new file mode 100644 index 0000000..d80284e --- /dev/null +++ b/src/test/java/model/LadderUsersTest.java @@ -0,0 +1,70 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static fixture.LadderUserFixture.DUPLICATED_USER_COLLECTION; +import static fixture.LadderUserFixture.FIVE_USER_COLLECTION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LadderUsersTest { + + private static final String DEFAULT_USER_NAME = "hello"; + + @Test + @DisplayName("사용자가 2명 미만이면 예외가 발생한다") + void ifFewUserThenThrowException() { + List singleLadderUserCollection = createLadderUserCollection(DEFAULT_USER_NAME); + + assertThatThrownBy(() -> new LadderUsers(singleLadderUserCollection)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("사용자 이름이 중복되면 예외가 발생한다") + void ifNameDuplicatedThenThrowException() { + List duplicatedUserCollection = DUPLICATED_USER_COLLECTION.getValueCollection(); + + assertThatThrownBy(() -> new LadderUsers(duplicatedUserCollection)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("사용자 이름을 담은 컬렉션을 반환한다") + void getNames() { + List fiveLadderUserCollection = FIVE_USER_COLLECTION.getValueCollection(); + LadderUsers ladderUsers = new LadderUsers(fiveLadderUserCollection); + List names = ladderUsers.getNames(); + + int actualSize = names.size(); + int expectedSize = fiveLadderUserCollection.size(); + + assertThat(actualSize).isSameAs(expectedSize); + } + + @Test + @DisplayName("이름을 통해 인덱스 번호를 반환한다") + void getIndexFromName() { + List ladderUserCollection = createLadderUserCollection("1", "2", "3", "4", "5"); + LadderUsers ladderUsers = new LadderUsers(ladderUserCollection); + + for (LadderUser ladderUser : ladderUserCollection) { + int actualIndex = ladderUsers.findIndexOfUserByName(ladderUser.getName()) + .orElseThrow(); + int expectedIndex = ladderUserCollection.indexOf(ladderUser); + + assertThat(actualIndex).isSameAs(expectedIndex); + } + } + + private List createLadderUserCollection(String... names) { + return Arrays.stream(names) + .map(LadderUser::new) + .toList(); + } + +} diff --git a/src/test/java/model/LineTest.java b/src/test/java/model/LineTest.java new file mode 100644 index 0000000..5922769 --- /dev/null +++ b/src/test/java/model/LineTest.java @@ -0,0 +1,23 @@ +package model; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +class LineTest { + + private static final int DEFAULT_SIZE = 5; + private static final LinksGenerator DEFAULT_LINKS_GENERATOR = new DetachedRandomLinksGenerator(DEFAULT_SIZE); + + @Test + @DisplayName("내부적으로 지니고 있는 List를 반환한다") + void getLinks() { + Line line = new Line(DEFAULT_LINKS_GENERATOR); + List links = line.getLinks(); + + Assertions.assertThat(links.size()).isSameAs(DEFAULT_SIZE); + } + +} diff --git a/src/test/java/model/LinkTest.java b/src/test/java/model/LinkTest.java new file mode 100644 index 0000000..bd9c3ca --- /dev/null +++ b/src/test/java/model/LinkTest.java @@ -0,0 +1,72 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static model.LinkStatus.*; +import static org.assertj.core.api.Assertions.assertThat; + +class LinkTest { + + private static final boolean DEFAULT_CONNECT_DECIDER = true; + private static final boolean TRUE_CONNECT_DECIDER = true; + private static final boolean FALSE_CONNECT_DECIDER = false; + private static final boolean DEFAULT_CONNECTABLE = true; + private static final boolean TRUE_CONNECTABLE = true; + private static final boolean FALSE_CONNECTABLE = false; + + @Test + @DisplayName("상태 값이 UNDEFINED인 Link 인스턴스를 생성한다") + void createUndefinedLink() { + Link undefinedLink = Link.getUndefinedLink(); + + assertThat(undefinedLink.getLinkstatus()).isSameAs(UNDEFINED); + } + + @Test + @DisplayName("상태 값이 UNDEFINED가 아닌 Link 인스턴스를 생성한다") + void createNotUndefinedLink() { + Link definedLink = Link.getDefinedLink(DEFAULT_CONNECT_DECIDER, DEFAULT_CONNECTABLE); + + assertThat(definedLink.getLinkstatus()).isNotSameAs(UNDEFINED); + } + + @Test + @DisplayName("전달 값이 둘 다 ture라면, 상태 값이 PRESENT인 Link 인스턴스를 생성한다") + void createPresentLink() { + Link link = Link.getDefinedLink(TRUE_CONNECT_DECIDER, TRUE_CONNECTABLE); + + assertThat(link.getLinkstatus()).isSameAs(PRESENT); + } + + @ParameterizedTest + @DisplayName("ConnectDecider가 false 값을 지닌다면, 상태 값이 ABSENT인 Link 인스턴스를 생성한다") + @ValueSource(booleans = {true, false}) + void createAbsentLinkByConnectDecider(boolean connectable) { + Link link = Link.getDefinedLink(FALSE_CONNECT_DECIDER, connectable); + + assertThat(link.getLinkstatus()).isSameAs(ABSENT); + } + + @ParameterizedTest + @DisplayName("connectable 값이 false라면, 상태 값이 ABSENT인 Link 인스턴스를 생성한다") + @ValueSource(booleans = {true, false}) + void createAbsentLinkByConnectable(boolean connectDecider) { + Link link = Link.getDefinedLink(connectDecider, FALSE_CONNECTABLE); + + assertThat(link.getLinkstatus()).isSameAs(ABSENT); + } + + @Test + @DisplayName("LinkStatus 값이 UNDEFINED인지 여부를 반환한다") + void returnIsUndefined() { + Link definedLink = Link.getDefinedLink(DEFAULT_CONNECT_DECIDER, DEFAULT_CONNECTABLE); + Link undefinedLink = Link.getUndefinedLink(); + + assertThat(definedLink.isUndefined()).isFalse(); + assertThat(undefinedLink.isUndefined()).isTrue(); + } + +} From 857cfc9a438f811add8d212455b4a3515f7e1d06 Mon Sep 17 00:00:00 2001 From: Kim Joon Date: Fri, 14 Mar 2025 13:58:32 +0900 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20isSameAs()=EB=A5=BC=20isEqualTo()?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/model/LadderUsersTest.java | 4 ++-- src/test/java/model/LineTest.java | 2 +- src/test/java/model/LinkTest.java | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/model/LadderUsersTest.java b/src/test/java/model/LadderUsersTest.java index d80284e..e7d6940 100644 --- a/src/test/java/model/LadderUsersTest.java +++ b/src/test/java/model/LadderUsersTest.java @@ -43,7 +43,7 @@ void getNames() { int actualSize = names.size(); int expectedSize = fiveLadderUserCollection.size(); - assertThat(actualSize).isSameAs(expectedSize); + assertThat(actualSize).isEqualTo(expectedSize); } @Test @@ -57,7 +57,7 @@ void getIndexFromName() { .orElseThrow(); int expectedIndex = ladderUserCollection.indexOf(ladderUser); - assertThat(actualIndex).isSameAs(expectedIndex); + assertThat(actualIndex).isEqualTo(expectedIndex); } } diff --git a/src/test/java/model/LineTest.java b/src/test/java/model/LineTest.java index 5922769..8307596 100644 --- a/src/test/java/model/LineTest.java +++ b/src/test/java/model/LineTest.java @@ -17,7 +17,7 @@ void getLinks() { Line line = new Line(DEFAULT_LINKS_GENERATOR); List links = line.getLinks(); - Assertions.assertThat(links.size()).isSameAs(DEFAULT_SIZE); + Assertions.assertThat(links.size()).isEqualTo(DEFAULT_SIZE); } } diff --git a/src/test/java/model/LinkTest.java b/src/test/java/model/LinkTest.java index bd9c3ca..88e13e0 100644 --- a/src/test/java/model/LinkTest.java +++ b/src/test/java/model/LinkTest.java @@ -22,7 +22,7 @@ class LinkTest { void createUndefinedLink() { Link undefinedLink = Link.getUndefinedLink(); - assertThat(undefinedLink.getLinkstatus()).isSameAs(UNDEFINED); + assertThat(undefinedLink.getLinkstatus()).isEqualTo(UNDEFINED); } @Test @@ -30,7 +30,7 @@ void createUndefinedLink() { void createNotUndefinedLink() { Link definedLink = Link.getDefinedLink(DEFAULT_CONNECT_DECIDER, DEFAULT_CONNECTABLE); - assertThat(definedLink.getLinkstatus()).isNotSameAs(UNDEFINED); + assertThat(definedLink.getLinkstatus()).isNotEqualTo(UNDEFINED); } @Test @@ -38,7 +38,7 @@ void createNotUndefinedLink() { void createPresentLink() { Link link = Link.getDefinedLink(TRUE_CONNECT_DECIDER, TRUE_CONNECTABLE); - assertThat(link.getLinkstatus()).isSameAs(PRESENT); + assertThat(link.getLinkstatus()).isEqualTo(PRESENT); } @ParameterizedTest @@ -47,7 +47,7 @@ void createPresentLink() { void createAbsentLinkByConnectDecider(boolean connectable) { Link link = Link.getDefinedLink(FALSE_CONNECT_DECIDER, connectable); - assertThat(link.getLinkstatus()).isSameAs(ABSENT); + assertThat(link.getLinkstatus()).isEqualTo(ABSENT); } @ParameterizedTest @@ -56,7 +56,7 @@ void createAbsentLinkByConnectDecider(boolean connectable) { void createAbsentLinkByConnectable(boolean connectDecider) { Link link = Link.getDefinedLink(connectDecider, FALSE_CONNECTABLE); - assertThat(link.getLinkstatus()).isSameAs(ABSENT); + assertThat(link.getLinkstatus()).isEqualTo(ABSENT); } @Test