diff --git a/src/main/java/calculator/DotV1.java b/src/main/java/calculator/DotV1.java new file mode 100644 index 00000000..fcea73c8 --- /dev/null +++ b/src/main/java/calculator/DotV1.java @@ -0,0 +1,56 @@ +package calculator; + +import java.util.Objects; + +public class DotV1 { + private final int x; + private final int y; + private static final String INVALID_INPUT_FORMAT = "Invalid input format"; + private static final String REGEX_FOR_STRING_INPUT = "[()]"; + private static final int MAX_LOCATION_FOR_DOT = 24; + public DotV1(int x, int y) { + validateLocations(x, y); + this.x = x; + this.y = y; + } + + public DotV1(String input) { + String[] parts = input.replaceAll(REGEX_FOR_STRING_INPUT, "").split(","); + if (parts.length != 2) { + throw new IllegalArgumentException(INVALID_INPUT_FORMAT); + } + int x = Integer.parseInt(parts[0].trim()); + int y = Integer.parseInt(parts[1].trim()); + validateLocations(x, y); + this.x = x; + this.y = y; + } + + private void validateLocations(int x, int y) { + if (x > MAX_LOCATION_FOR_DOT || y > MAX_LOCATION_FOR_DOT) throw new IllegalArgumentException(); + } + + public double getDistanceBetween(DotV1 secondDotV1) { + return Math.sqrt(Math.pow((secondDotV1.x - this.x), 2) + Math.pow((secondDotV1.y - this.y), 2)); + } + public boolean isXParallel(DotV1 dotV1) { + return this.y == dotV1.y; + } + + public boolean isYParallel(DotV1 dotV1) { + return this.x == dotV1.x; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DotV1 dotV1 = (DotV1) o; + return x == dotV1.x && y == dotV1.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } +} diff --git a/src/main/java/calculator/Dots.java b/src/main/java/calculator/Dots.java new file mode 100644 index 00000000..ce1bf2d7 --- /dev/null +++ b/src/main/java/calculator/Dots.java @@ -0,0 +1,141 @@ +package calculator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Dots { + private final List dotV1s; + + /** + * CONSTANTS + */ + private static final String SPLIT_EXPRESSION = "-"; + private static final String IS_NOT_LINE = "선이 아니다."; + private static final String IS_NOT_SQUARE = "사각형이 아니다."; + + public Dots(String input) { + validateInput(input); + this.dotV1s = Arrays.stream(input.split("-")).map(DotV1::new).collect(Collectors.toList()); + } + + private Dots(DotV1 first, DotV1 second){ + this.dotV1s = new ArrayList<>(); + dotV1s.add(first); + dotV1s.add(second); + } + + public static Dots asLine(DotV1 first, DotV1 second){ + return new Dots(first, second); + } + + /** + * validations & common methods + */ + private void validateInput(String input) { + if (!input.contains(SPLIT_EXPRESSION)){ + throw new IllegalArgumentException(); + } + } + + public int size() { + return this.dotV1s.size(); + } + + /** + * line methods + */ + public boolean isLine() { + return this.dotV1s.size() == 2; + } + + public boolean isXParallelLine(){ + if (!isLine()) { + throw new IllegalArgumentException(IS_NOT_LINE); + } + return dotV1s.get(0).isXParallel(dotV1s.get(1)); + } + + public boolean isYParallelLine(){ + if (!isLine()) { + throw new IllegalArgumentException(IS_NOT_LINE); + } + return dotV1s.get(0).isYParallel(dotV1s.get(1)); + } + + public double getDistanceBetween(){ + if (!this.isLine()) { + throw new IllegalArgumentException(IS_NOT_LINE); + } + return this.dotV1s.get(0).getDistanceBetween(dotV1s.get(1)); + } + + /** + * square methods + */ + public boolean isSquare() { + if (this.dotV1s.size() != 4) { + return false; + } + return isAllParallel(); + } + + public boolean isAllParallel() { + return IntStream.range(0, 4).allMatch(i -> { + DotV1 dotV1 = dotV1s.get(i); + DotV1 dotV12 = dotV1s.get((i + 1) % 4); // 다음 점 + return dotV1.isXParallel(dotV12) || dotV1.isYParallel(dotV12); + }); + } + + public int getSquareArea(){ + if (!isSquare()) { + throw new IllegalArgumentException(IS_NOT_SQUARE); + } + + List collect = IntStream.range(0, 4) + .mapToObj(i -> dotV1s.get(i).getDistanceBetween(dotV1s.get((i + 1) % 4))) + .distinct().collect(Collectors.toList()); + + if (collect.size() == 1) return (int) Math.pow(collect.get(0), 2); + return (int) (collect.get(0) * collect.get(1)); + } + + /** + * triangle + */ + public boolean isTriangle() { + return this.dotV1s.size() == 3; + } + + public double getLinesLength(){ + double lineLength = 0; + for (int i = 0; i < this.dotV1s.size(); i++) { + DotV1 first = dotV1s.get(i); + DotV1 second = dotV1s.get((i + 1) % dotV1s.size()); + LineV1 lineV1 = new LineV1(first, second); + lineLength += lineV1.getDotsDistance(); + } + return lineLength; + } + + //헤론의 공식 + //s = (a + b + c) / 2 + //Area = √(s * (s - a) * (s - b) * (s - c)) + public double getTriangleArea(){ + List lineLengths = new ArrayList<>(); + for (int i = 0; i < this.dotV1s.size(); i++) { + DotV1 first = dotV1s.get(i); + DotV1 second = dotV1s.get((i + 1) % dotV1s.size()); + LineV1 lineV1 = new LineV1(first, second); + lineLengths.add(lineV1.getDotsDistance()); + } + double area = getLinesLength() / 2; + for (Double lineLength : lineLengths) { + area *= (getLinesLength() / 2) - lineLength; + } + return Math.sqrt(area); + } +} \ No newline at end of file diff --git a/src/main/java/calculator/LineV1.java b/src/main/java/calculator/LineV1.java new file mode 100644 index 00000000..0fdf8237 --- /dev/null +++ b/src/main/java/calculator/LineV1.java @@ -0,0 +1,25 @@ +package calculator; + +public class LineV1 { + private final Dots dots; + + public LineV1(Dots dots) { + this.dots = dots; + } + + public LineV1(DotV1 first, DotV1 second){ + this.dots = Dots.asLine(first, second); + } + + public double getDotsDistance(){ + return this.dots.getDistanceBetween(); + } + + public boolean isXParallel() { + return this.dots.isXParallelLine(); + } + + public boolean isYParallel(){ + return this.dots.isYParallelLine(); + } +} \ No newline at end of file diff --git a/src/main/java/calculator/SquareV1.java b/src/main/java/calculator/SquareV1.java new file mode 100644 index 00000000..da316fc9 --- /dev/null +++ b/src/main/java/calculator/SquareV1.java @@ -0,0 +1,16 @@ +package calculator; + +public class SquareV1 { + private final Dots dots; + + public SquareV1(Dots dots) { + if (!dots.isSquare()){ + throw new IllegalArgumentException(); + } + this.dots = dots; + } + + public int getArea() { + return this.dots.getSquareArea(); + } +} diff --git a/src/main/java/calculator/TriangleV1.java b/src/main/java/calculator/TriangleV1.java new file mode 100644 index 00000000..69b503ef --- /dev/null +++ b/src/main/java/calculator/TriangleV1.java @@ -0,0 +1,5 @@ +package calculator; + +public class TriangleV1 { + +} diff --git a/src/main/java/figure/AbstractFigure.java b/src/main/java/figure/AbstractFigure.java new file mode 100644 index 00000000..a2499d6b --- /dev/null +++ b/src/main/java/figure/AbstractFigure.java @@ -0,0 +1,21 @@ +package figure; + +import java.util.List; + +public abstract class AbstractFigure implements Figure{ + private final List dotList; + + protected AbstractFigure(List dotList) { + if (dotList.size() != this.size()){ + throw new IllegalArgumentException(); + } + this.dotList = dotList; + } + public Dot getDot(int index){ + return this.dotList.get(index); + } + @Override + public List getDots(){ + return this.dotList; + } +} diff --git a/src/main/java/figure/Dot.java b/src/main/java/figure/Dot.java new file mode 100644 index 00000000..ab4d1755 --- /dev/null +++ b/src/main/java/figure/Dot.java @@ -0,0 +1,56 @@ +package figure; + +import java.util.Objects; + +public class Dot { + private final int x; + private final int y; + private static final String INVALID_INPUT_FORMAT = "Invalid input format"; + private static final String REGEX_FOR_STRING_INPUT = "[()]"; + private static final int MAX_LOCATION_FOR_DOT = 24; + private Dot(int x, int y) { + validateLocations(x, y); + this.x = x; + this.y = y; + } + public static Dot ofTest(int x, int y){ + return new Dot(x,y); + } + public static Dot of(String input){ + String[] parts = input.replaceAll(REGEX_FOR_STRING_INPUT, "").split(","); + if (parts.length != 2) { + throw new IllegalArgumentException(INVALID_INPUT_FORMAT); + } + int x = Integer.parseInt(parts[0].trim()); + int y = Integer.parseInt(parts[1].trim()); + return new Dot(x, y); + } + + private void validateLocations(int x, int y) { + if (x > MAX_LOCATION_FOR_DOT || y > MAX_LOCATION_FOR_DOT) throw new IllegalArgumentException(); + } + + public double getDistanceBetween(Dot secondDot) { + return Math.sqrt(Math.pow((secondDot.x - this.x), 2) + Math.pow((secondDot.y - this.y), 2)); + } + public boolean isXParallel(Dot dot) { + return this.y == dot.y; + } + + public boolean isYParallel(Dot dot) { + return this.x == dot.x; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Dot dot = (Dot) o; + return x == dot.x && y == dot.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } +} diff --git a/src/main/java/figure/Figure.java b/src/main/java/figure/Figure.java new file mode 100644 index 00000000..9c7d58d8 --- /dev/null +++ b/src/main/java/figure/Figure.java @@ -0,0 +1,10 @@ +package figure; + +import java.util.List; + +public interface Figure { + String getName(); + int size(); + double getArea(); + List getDots(); +} diff --git a/src/main/java/figure/FigureEnum.java b/src/main/java/figure/FigureEnum.java new file mode 100644 index 00000000..5fe7acb2 --- /dev/null +++ b/src/main/java/figure/FigureEnum.java @@ -0,0 +1,22 @@ +package figure; + +public enum FigureEnum { + LINE("Line", 2), + TRIANGLE("Triangle", 3), + SQUARE("Square", 4); + + private final String name; + private final int size; + FigureEnum(String name, int size) { + this.name = name; + this.size = size; + } + + public String getName() { + return name; + } + + public int getSize(){ + return this.size; + } +} diff --git a/src/main/java/figure/FigureFactory.java b/src/main/java/figure/FigureFactory.java new file mode 100644 index 00000000..cfaa507e --- /dev/null +++ b/src/main/java/figure/FigureFactory.java @@ -0,0 +1,21 @@ +package figure; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FigureFactory { + private final List
figureList; + private static final Map figureMap = new HashMap<>(); + + public FigureFactory(List
figureList) { + this.figureList = figureList; + for (Figure figure : figureList) { + figureMap.put(figure.size(), figure); + } + } + + static Figure getInstance(List points) { + return figureMap.get(points.size()); + } +} \ No newline at end of file diff --git a/src/main/java/figure/Line.java b/src/main/java/figure/Line.java new file mode 100644 index 00000000..8cfa9ebf --- /dev/null +++ b/src/main/java/figure/Line.java @@ -0,0 +1,31 @@ +package figure; + +import java.util.List; + +public class Line extends AbstractFigure { + + protected Line(List dotList) { + super(dotList); + } + + @Override + public String getName() { + return FigureEnum.LINE.getName(); + } + + @Override + public int size() { + return FigureEnum.LINE.getSize(); + } + + @Override + public double getArea() { + return getLineDistance(); + } + + private double getLineDistance() { + Dot dot = this.getDot(0); + Dot secondDot = this.getDot(1); + return dot.getDistanceBetween(secondDot); + } +} diff --git a/src/main/java/figure/Square.java b/src/main/java/figure/Square.java new file mode 100644 index 00000000..00751769 --- /dev/null +++ b/src/main/java/figure/Square.java @@ -0,0 +1,40 @@ +package figure; + +import calculator.DotV1; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Square extends AbstractFigure{ + + protected Square(List dotList) { + super(dotList); + } + + @Override + public String getName() { + return FigureEnum.SQUARE.getName(); + } + + @Override + public int size() { + return FigureEnum.SQUARE.getSize(); + } + + @Override + public double getArea() { + return getSquareArea(); + } + + private double getSquareArea(){ + List collect = IntStream.range(0, 4) + .mapToObj(i -> getDot(i).getDistanceBetween(getDots().get((i + 1) % 4))) + .distinct().collect(Collectors.toList()); + + if (collect.size() == 1) return (int) Math.pow(collect.get(0), 2); + + return collect.get(0) * collect.get(1); + } + +} diff --git a/src/main/java/figure/Triangle.java b/src/main/java/figure/Triangle.java new file mode 100644 index 00000000..31310198 --- /dev/null +++ b/src/main/java/figure/Triangle.java @@ -0,0 +1,46 @@ +package figure; + +import java.util.Arrays; +import java.util.List; + +public class Triangle extends AbstractFigure{ + protected Triangle(List dotList) { + super(dotList); + } + + @Override + public String getName() { + return FigureEnum.TRIANGLE.getName(); + } + + @Override + public int size() { + return FigureEnum.TRIANGLE.getSize(); + } + + @Override + public double getArea() { + return calculateArea(); + } + private double getLineLength(int firstIdx, int secondIdx){ + Dot dot = getDot(firstIdx); + Dot secondDot = getDot(secondIdx); + return dot.getDistanceBetween(secondDot); + } + + //헤론의 공식 + //s = (a + b + c) / 2 + //Area = √(s * (s - a) * (s - b) * (s - c)) + private double calculateArea(){ + double[] lengths = new double[3]; + + for (int i = 0; i < size(); i++) { + lengths[i] = this.getLineLength(i, (i + 1) % 3); + } + + double s = Arrays.stream(lengths).sum() / 2; + + return Math.sqrt(s * (s - lengths[0]) * (s - lengths[1]) * (s - lengths[2])); + } + +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..ae2791fb --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,4 @@ +package view; + +public class InputView { +} diff --git a/src/test/java/DotV1Test.java b/src/test/java/DotV1Test.java new file mode 100644 index 00000000..ff2a0d46 --- /dev/null +++ b/src/test/java/DotV1Test.java @@ -0,0 +1,59 @@ +import calculator.DotV1; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class DotV1Test { + //- 사용자가 점에 대한 좌표 정보를 입력하는 메뉴를 구성한다. + //- 좌표 정보는 괄호"(", ")"로 둘러쌓여 있으며 쉼표(,)로 x값과 y값을 구분한다. + //- X, Y좌표 모두 최대 24까지만 입력할 수 있다. + + //- 정상적인 좌표값을 입력한 경우, 해당 좌표에 특수문자를 표시한다. + //- 좌표값을 두 개 입력한 경우, 두 점을 있는 직선으로 가정한다. 좌표값과 좌표값 사이는 '-' 문자로 구분한다. + //- 직선인 경우는 두 점 사이 거리를 계산해서 출력한다. + + @Test + void dotInit_test() { + DotV1 dotV1 = new DotV1(1, 1); + assertThat(dotV1).isEqualTo(new DotV1(1, 1)); + } + + @Test + void dot_validation_test() { //- 입력 범위를 초과할 경우 에러 문구를 출력하고 다시 입력을 받는다. 중요! + assertThatThrownBy(() -> new DotV1(25, 25)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void string_input_test() { + String input = "(1,1)"; + DotV1 dotV1 = new DotV1(input); + assertThat(dotV1).isEqualTo(new DotV1(1,1)); + } + + @Test + void validate_input_test() { + String input = "(25,25)"; + assertThatThrownBy(() -> new DotV1(input)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void get_dot_distance() { + DotV1 dotV1 = new DotV1(10,10); + DotV1 secondDotV1 = new DotV1(14,15); + assertThat(dotV1.getDistanceBetween(secondDotV1)).isEqualTo(6.4, offset(0.1)); + } + + @Test + void is_x_parallel() { + DotV1 dotV1 = new DotV1(10, 10); + DotV1 dotV12 = new DotV1(15, 10); + assertThat(dotV1.isXParallel(dotV12)).isTrue(); + } + + @Test + void is_y_parallel() { + DotV1 dotV1 = new DotV1(10, 10); + DotV1 dotV12 = new DotV1(10, 15); + assertThat(dotV1.isYParallel(dotV12)).isTrue(); + } +} diff --git a/src/test/java/DotsTest.java b/src/test/java/DotsTest.java new file mode 100644 index 00000000..048ceace --- /dev/null +++ b/src/test/java/DotsTest.java @@ -0,0 +1,67 @@ +import calculator.Dots; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class DotsTest { + @Test + void dots_init_test() { + String input = "(10,10)-(22,10)-(22,18)-(10,18)"; + Dots dots = new Dots(input); + assertThat(dots.size()).isEqualTo(4); + } + + @Test + void is_line() { + String input = "(10,10)-(22,10)"; + Dots dots = new Dots(input); + assertThat(dots.isLine()).isTrue(); + } + + @Test + void invalid_lineInput_test() { + String input = "(1,1)1(5,5)"; + String input2 = "(1,1)"; + String input3 = "(25,25)-(25,25)"; + + assertThatThrownBy(() -> new Dots(input)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new Dots(input2)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new Dots(input3)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void is_square() { + String input = "(10,10)-(22,10)-(22,18)-(10,18)"; + Dots dots = new Dots(input); + assertThat(dots.isSquare()).isTrue(); + } + + @Test + void is_not_square() { + String input = "(10,11)-(22,10)-(22,18)-(10,18)"; + Dots dots = new Dots(input); + assertThat(dots.isSquare()).isFalse(); + } + + @Test + void is_triangle() { + String input = "(10,11)-(22,10)-(22,18)"; + Dots dots = new Dots(input); + assertThat(dots.isTriangle()).isTrue(); + } + + @Test + void is_not_triangle() { + String input = "(10,11)-(22,10)"; + Dots dots = new Dots(input); + assertThat(dots.isTriangle()).isFalse(); + } + + @Test + void triangle_area() { + String input = "(10,11)-(22,10)-(22,18)"; + Dots dots = new Dots(input); + + } +} diff --git a/src/test/java/LineV1Test.java b/src/test/java/LineV1Test.java new file mode 100644 index 00000000..82b75a34 --- /dev/null +++ b/src/test/java/LineV1Test.java @@ -0,0 +1,38 @@ +import calculator.Dots; +import calculator.LineV1; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class LineV1Test { + //- 사용자가 점에 대한 좌표 정보를 입력하는 메뉴를 구성한다. + //- 좌표 정보는 괄호"(", ")"로 둘러쌓여 있으며 쉼표(,)로 x값과 y값을 구분한다. + //- X, Y좌표 모두 최대 24까지만 입력할 수 있다. + + //- 정상적인 좌표값을 입력한 경우, 해당 좌표에 특수문자를 표시한다. + //- 좌표값을 두 개 입력한 경우, 두 점을 있는 직선으로 가정한다. 좌표값과 좌표값 사이는 '-' 문자로 구분한다. + //- 직선인 경우는 두 점 사이 거리를 계산해서 출력한다. + + // getter 매소드를 두는 것이 좋지 않다고 판단됨. lineGetFirstDot() 과 같은 형태는 쓰지 않기로 - + + @Test + void lineDistance_test() { + String input = "(1,1)-(5,5)"; + LineV1 lineV1 = new LineV1(new Dots(input)); + assertThat(lineV1.getDotsDistance()).isEqualTo(5.65, offset(0.01)); + } + + @Test + void is_line_x_parallel() { + String input = "(1,1)-(5,1)"; // y 값이 같다. + LineV1 lineV1 = new LineV1(new Dots(input)); + assertThat(lineV1.isXParallel()).isTrue(); + } + + @Test + void is_line_y_parallel() { + String input = "(1,1)-(1,5)"; // x 값이 같다. + LineV1 lineV1 = new LineV1(new Dots(input)); + assertThat(lineV1.isYParallel()).isTrue(); + } +} diff --git a/src/test/java/SquareV1Test.java b/src/test/java/SquareV1Test.java new file mode 100644 index 00000000..92a1cab6 --- /dev/null +++ b/src/test/java/SquareV1Test.java @@ -0,0 +1,19 @@ +import calculator.Dots; +import calculator.SquareV1; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class SquareV1Test { + //- 좌표값을 두 개 입력한 경우, 두 점을 있는 직선으로 가정한다. 좌표값과 좌표값 사이는 '-' 문자로 구분한다. + //- 좌표값을 네 개 입력한 경우, 네 점을 연결하는 사각형으로 가정한다. + // - 네 점이 뒤틀어진 사다리꼴이나 마름모는 제외하고 직사각형만 허용하도록 검사한다. + // - 사각형인 경우 사각형의 넓이를 계산해서 출력한다 + @Test + void square_area_test() { + String input = "(10,10)-(22,10)-(22,18)-(10,18)"; + Dots dots = new Dots(input); + SquareV1 squareV1 = new SquareV1(dots); + assertThat(squareV1.getArea()).isEqualTo(12 * 8); + } +} diff --git a/src/test/java/TriangleV1Test.java b/src/test/java/TriangleV1Test.java new file mode 100644 index 00000000..723ca77f --- /dev/null +++ b/src/test/java/TriangleV1Test.java @@ -0,0 +1,28 @@ +import calculator.Dots; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.offset; + +class TriangleV1Test { + @Test + void is_triangle() { + String input = "(10,11)-(22,10)-(22,18)"; + Dots dots = new Dots(input); + assertThat(dots.isTriangle()).isTrue(); + } + + @Test + void get_triangle_distances() { + String input = "(10,11)-(22,10)-(22,18)"; + Dots dots = new Dots(input); + assertThat(dots.getLinesLength()).isEqualTo(33.93, offset(0.01)); + } + + @Test + void get_triangle_area() { + String input = "(10,11)-(22,10)-(22,18)"; + Dots dots = new Dots(input); + assertThat(dots.getTriangleArea()).isEqualTo(48, offset(0.1)); + } +}