Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[자동자 경주 게임] 이준섭 미션 제출합니다. #533

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
94 changes: 93 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,99 @@
package racingcar;

import camp.nextstep.edu.missionutils.Console;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Application {
public static void main(String[] args) {
// TODO 구현 진행
List<Car> cars = createCars();

int tryCount = receiveTryCount();

runRacingGame(cars, tryCount);

printWinners(findWinners(cars));
}

private static List<Car> createCars() {
List<String> carNames = null;
List<Car> cars = new ArrayList<>();
while (carNames == null) {
System.out.println("경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)");
String input = Console.readLine();
try {
carNames = Arrays.asList(input.split(","));
carNames.forEach(inputName -> cars.add(new Car(inputName))); // carNames 리스트의 각 요소를 inputName으로 받아서 객체를 생성하고 cars 리스트에 추가함.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

적절한 forEach 활용 좋네요! Collection 인터페이스의 api들은 많이 알면 알수록 도움이 됩니다!

break;
} catch (IllegalArgumentException e) {
System.out.println("[ERROR] 올바른 형식의 자동차 이름을 입력하세요.");
}
}
return cars;
}

private static int receiveTryCount() {
int tryCount = 0;
while (tryCount == 0) {
System.out.println("시도할 횟수는 몇 회인가요?");
String input = Console.readLine();
try {
tryCount = Integer.parseInt(input); // 문자열을 정수로 변환하는 문법
validateTryCount(tryCount);
break;
}
catch (IllegalArgumentException e) {
System.out.println("[ERROR] 올바른 형식의 숫자를 입력하세요.");
}
}
return tryCount;
}

private static void validateTryCount(int tryCount) {
if (tryCount <= 0) {
throw new IllegalArgumentException();
}
}

private static void runRacingGame(List<Car> cars, int tryCount) {
System.out.println("\n실행 결과");
for (int i = 0; i < tryCount; i++) {
for (Car car : cars) {
car.moveForward();
}
printRoundResult(cars);
}
}

private static void printRoundResult(List<Car> cars) {
for (Car car : cars) {
System.out.println(car.getName() + " : " + new String(new char[car.getPosition()]).replace('\0', '-'));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
System.out.println(car.getName() + " : " + new String(new char[car.getPosition()]).replace('\0', '-'));
System.out.println(car.name + " : " + new String(new char[car.getPosition()]).replace('\0', '-'));

Car 클래스의 name 필드 접근 제어자를 private에서 public 으로 변경하면 다음과 같이 코드를 수정할 수 있습니다.
그런데 문제에는 이런 요구사항이 있었죠.
name, position 변수의 접근 제어자인 private을 변경할 수 없다.

왜 필드를 private으로 사용해야할까요?

Copy link
Author

@junseoplee junseoplee Jan 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

멤버 변수로의 직접 접근은 좋지 않습니다. 객체 지향 프로그래밍의 원칙 중 하나인 캡슐화에 위배됩니다.
캡슐화는 외부에서의 직접 접근을 막고, 객체에 접근하는 메서드를 통해서만 접근이 가능하게 합니다. 정보를 숨겨 안정성을 높이고 유지보수, 재사용에 용이하게 합니다.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왜 외부에서 직접 접근하면 안될까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

외부에서 직접 접근하는 메서드가 많아진다면 추후 클래스의 구성이나 구현을 변경할 때 수정이 어려워지기 때문입니다!

}
System.out.println();
}

private static List<String> findWinners(List<Car> cars) {
int maxPosition = getMaxPosition(cars);
List<String> winners = new ArrayList<>();
for (Car car : cars) {
if (car.getPosition() == maxPosition) {
winners.add(car.getName());
}
}
return winners;
}

private static int getMaxPosition(List<Car> cars) {
int maxPosition = 0;
for (Car car : cars) {
maxPosition = Math.max(maxPosition, car.getPosition());
}
return maxPosition;
}

private static void printWinners(List<String> winners) {
System.out.println("최종 우승자 : " + String.join(", ", winners));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

적절한 함수활용 굿👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String.join은 문자열을 결합할 때 사용합니다.
delimiter(,)로 구분하여 요소들을 하나의 문자열로 결합합니다.

}
}
32 changes: 28 additions & 4 deletions src/main/java/racingcar/Car.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
package racingcar;

import camp.nextstep.edu.missionutils.Randoms;

public class Car {
static final int MAX_NAME_LENGTH = 5;
private final String name;
private int position = 0;
private int position;

public Car(String inputName) {
validateCarNames(inputName);
this.name = inputName;
this.position = 0;
}
Comment on lines +10 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋습니다. 이렇게 했을 때 장점이 뭘까요!?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

객체가 사용되는 동안 유효한 상태를, 일관성을 보장합니다.
다른 메서드에서 유효성 검사를 추가로 할 필요가 없고, 이것은 중복 코드를 방지할 수 있습니다.
에러가 발생한 경우 문제가 어느 곳에 있는지 파악하기 쉬워집니다. 즉, 유지 보수에 용이합니다.


public void moveForward() {
int randomValue = Randoms.pickNumberInRange(0,9);
if (randomValue >= 4) {
position++;
}
Comment on lines +17 to +20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 바꾸기 vs 그냥 쓰기
각각 어떤 장단점이 있을까요?

Suggested change
int randomValue = Randoms.pickNumberInRange(0,9);
if (randomValue >= 4) {
position++;
}
if (Randoms.pickNumberInRange(0,9) >= 4) {
position++;
}

Copy link
Author

@junseoplee junseoplee Dec 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

바꿨을 때의 장단점

장점:
코드가 간결해집니다. 한 번 사용되는 간단한 로직일 경우에, 중복 코드를 최소화할 수 있습니다.
단점:
매번 Randoms.pickNumberInRange를 호출하기 때문에 성능상 단점이 있을 수 있습니다.
코드 한 줄에 많은 동작이 있다면 가독성이 감소할 수 있습니다. 복잡하고 다양한 동작을 수행할 경우에 이해하기 어려워질 수 있습니다.
이어져 코드가 복잡해지면 유지보수가 어려워질 수 있습니다.

randomValue 변수를 사용한다면 코드를 읽기가 더 쉽습니다. 다른 부분에서 동일한 난수를 사용한다면 재사용에 용이합니다. 또한 난수 생성 로직이 변경되더라도 해당 부분만 수정하면 됩니다.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정리 깔끔하네요! 그런데 매번 호출하는건 위 아래 전부 동일에서 성능은 사실상 동일합니다!
현재 코드에선 각각 가독성, 코드 길이에서 �우위가 있겠네요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다! 재사용할 수 있다는 것에 초점을 두고 이런 답변을 드렸네요! 현재 코드에서 성능은 동일합니다!

}

public String getName() {
return name;
}

public Car(String name) {
this.name = name;
public int getPosition() {
return position;
}

// 추가 기능 구현
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요없는 주석삭제 👍👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요 없는 주석은 삭제!

private void validateCarNames(String inputName) {
if (inputName.isEmpty() || inputName.length() > MAX_NAME_LENGTH) {
throw new IllegalArgumentException();
}
}
}