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

Conversation

junseoplee
Copy link

🚀 기능 요구사항
초간단 자동차 경주 게임을 구현한다.

주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.
자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.
아래의 프로그래밍 실행 결과 예시와 동일하게 입력과 출력이 이루어져야 한다.

Copy link

@poi1649 poi1649 left a comment

Choose a reason for hiding this comment

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

프로그래밍 이제 시작하는 사람 맞나.. 굉장히 훌륭하게 코드 작성해주셨네요 💯💯💯
제 생각엔 언어의 기초(int가 32bit, 변수의 스코프 등등...)는 이미 잡혀있는 것 같아, 일단 이번 단계에서는 요구사항에 있는 것들을 최대한 지키는 것을 목표로 진행해보려고 합니다.
(기능 요구사항뿐 아니라 프로그래밍 요구사항 과제 진행 요구사항등등 전부 준수하려고 노력해주세요!)

그래서 우선 요구사항에 관련한 커멘트 몇 개 남겼으니 확인하시고 답변 달아주세요!
화이팅 화이팅~

@@ -1,5 +1,7 @@
package racingcar;

import camp.nextstep.edu.missionutils.Randoms;

public class Car {
private final String name;
private int position = 0;
Copy link

Choose a reason for hiding this comment

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

  1. int와 Integer의 차이는 뭘까요?
  2. 이렇게 멤버 변수를 선언하며 직접 초기화 해주는 것과 생성자에서 초기화 해주는 것 어떤 차이가 있을까요?

생성자 예시

public Car(String name) {
    this.name = name;
    this.position = 0;
}

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.

  1. int와 integer의 차이
    -> int (기본 자료형)
    int는 기본적인 정수 데이터 타입으로, 자바의 8가지 기본 데이터 타입 중 하나입니다.
    int는 정수 값을 저장하며, 32비트로 표현됩니다.
    null 값이나 기타 메서드를 호출할 수 없습니다.
    -> Integer (래퍼 클래스)
    Integer는 int의 래퍼 클래스로, 정수를 객체로 감싸는 역할을 합니다.
    객체 지향적인 프로그래밍에서 int를 객체로 다룰 수 있도록 만든 클래스입니다.

Integer 클래스는 int의 기능을 확장하여 여러 유용한 메서드를 제공합니다.
null을 포함한 모든 객체의 메서드를 호출할 수 있습니다.

어떤 메서드들이 있을까?
해당 과제에서 사용했던 Integer.parseInt(input); 를 예로 들어 문자열을 정수로 변환하는 등 여러 메서드를 사용할 수 있습니다.

  1. 멤버 변수를 선언하며 직접 초기화 하는 것과 생성자에서 초기화하는 것의 차이
    직접 초기화 -> 해당 클래스의 모든 인스턴스가 같은 초기값을 가집니다.
    생성자에서 초기화 -> 객체를 초기화 할 때. 즉, 객체를 생성할 때 마다 서로 다른 초기값을 설정할 수 있습니다.

Copy link

@poi1649 poi1649 Jan 2, 2024

Choose a reason for hiding this comment

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

정리 좋네요!
2번에서 한 가지만 제 의견을 말씀드리자면, 저는 멤버 변수를 초기화하는 로직이 여기저기 퍼져있을 때 찾기 힘들어지는 경우가 생기기도 하는 것 같아요.(코드가 복잡해질수록 더더욱)
때문에 웬만한 경우 생성자에서 모든 멤버 변수를 초기화 하는 방법으로 사용합니다.
이런 부분에 대해 매번 고민하는 것도 자원 낭비라고 생각하기 때문에 이 기회에 본인만의 규칙을 세워봐도 좋겠네요!

Copy link
Author

Choose a reason for hiding this comment

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

그렇군요! 멤버 변수는 생성자에서 초기화 하도록 해야겠습니다!

@@ -8,5 +10,18 @@ public Car(String name) {
this.name = name;
}

// 추가 기능 구현
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.

필요 없는 주석은 삭제!

Comment on lines +14 to +17
int randomValue = Randoms.pickNumberInRange(0,9);
if (randomValue >= 4) {
position++;
}
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.

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

Comment on lines 48 to 51
Set<String> uniqueNames = new HashSet<>(carNames); // 중복을 허용하지 않는 자료구조
if (uniqueNames.size() < carNames.size()) { // 작다면 중복된 이름이 있다는 것을 의미함
throw new IllegalArgumentException();
}
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.

HashSet은 중복을 허용하지 않는 집합을 나타내는 자료구조입니다.
if (uniqueNames.size() < carNames.size()) {
-> 중복을 허용하지 않는 HashSet은 중복된 값들을 하나로 처리하기 때문에 크기가 작다면 중복된 값이 있다는 뜻입니다.

// 한 라운드 결과 출력
private static void printRoundResult(List<Car> cars) {
for (Car car : cars) {
// System.out.println(car.getName() + " : " + "-".repeat(car.getPosition()));
Copy link

Choose a reason for hiding this comment

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

안쓰는 코드 삭제해주세요!22

Copy link
Author

Choose a reason for hiding this comment

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

사용하지 않는 코드는 삭제!


// 자동차 이름 입력
private static List<String> getCarNames() {
// Scanner scanner = new Scanner(System.in);
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.

네!

Comment on lines 12 to 19
// 자동차 목록 생성
List<Car> cars = createCars(carNames);
// 시도 횟수 입력
int tryCount = getTryCount();
// 레이싱 게임 실행 -> 한 라운드 결과 출력
runRacingGame(cars, tryCount);
// 우승자 목록 찾기 -> 우승자 목록 출력
printWinners(findWinners(cars));
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 static int getTryCount() {
Copy link

Choose a reason for hiding this comment

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

일반적으로 get으로 시작하는 메서드를 자바에서는 getter 라고 부릅니다.
보통 getter의 역할을 클래스의 필드를 그대로 반환하는 것이라고 보는 개발자들이 많아요.
ex)

class Car {
    String name;

    public Car(String name) {
        this.name= name;
    }

    public String getName() {
        return this.name;
    }
}

물론 팀의 약속에 따라 달라질 수 있겠지만, 우리는 다른 개발자들과 협업해야하기 때문에 다른 개발자들이 익숙한 방식으로 코드를 작성함으로 협업의 효율을 높일 수도 있어요.

그럼 이 메서드 이름을 어떻게 수정해볼 수 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

입력 받는 메서드니까 receive로 바꿔보았습니다!

Copy link

Choose a reason for hiding this comment

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

좋습니다! 사소한 것 같지만 나중에 코드가 복잡해지고 여러 사람과 협업하게 될때 이런 규칙을 미리 만들고 가면 장점이 많다는 거 기억해두시면 좋을 것 같네요!


// 우승자 목록 출력
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(,)로 구분하여 요소들을 하나의 문자열로 결합합니다.

Comment on lines 43 to 47
for (String str : carNames) {
if (str.isEmpty() || str.length() > 5) {
throw new IllegalArgumentException();
}
}
Copy link

Choose a reason for hiding this comment

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

변수명으로 str보다 더 적합한 이름을 붙여줘도 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

Application에서 carNames을 이미 사용하고 있고, Car 클래스에도 멤버변수로 name이 있습니다.
여기에서는 입력된 자동차 이름을 나타내는 것이니까 inputName으로 변경한다면 보다 이해하기 쉬운 명확한 코드가 되리라 생각합니다.

Copy link

Choose a reason for hiding this comment

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

👍👍👍 이렇게 본인의 생각 잘 전달해주시는 게 리뷰할 때 많이 도움이 됩니다.

@junseoplee
Copy link
Author

과정 정리

Copy link

@poi1649 poi1649 left a comment

Choose a reason for hiding this comment

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

전체적으로 훌륭하게 의견을 정리하고 수정해주신 것 같네요!
GPT나 검색을 적절하게 사용하셔서 학습해주신 것 같은 생각이 들어요. 저는 굉장히 효율적이고 좋은 방법이라고 생각하지만, 이런 학습 방법에서 경계해야 할 것은 맹목적인 믿음입니다. 항상 어떤 방법에 대해서 장점, 단점을 스스로 생각하고 자신의 언어로 따로 정리해보는 습관을 가져보시는 것을 추천합니다!
고생 많으셨고 마지막으로 추가로 남긴 커멘트만 확인하고 수정 해주세요. 슬슬 다음 미션 시작해봅시다.


private static void validateCarNames(List<String> carNames) {
for (String inputName : carNames) {
if (inputName.isEmpty() || inputName.length() > 5) {
Copy link

Choose a reason for hiding this comment

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

Suggested change
if (inputName.isEmpty() || inputName.length() > 5) {
if (inputName.isEmpty() || inputName.length() > MAX_NAME_LENGTH) {

이렇게 클래스에 상수를 따로 선언해서 수정했을 때 어떤 이점이 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

코드가 길고 복잡해졌을 때 숫자 5가 무엇을 나타내는지 혼동이 올 수 있습니다.
이렇게 따로 상수를 선언했을 경우 보다 명확하고 이해하기 쉬워집니다.
또한 추후 값을 변경해야할 경우에 5를 각각 수정하는 것보다 선언해둔 상수를 수정하는 것이 편리하기 때문에 유지보수가 용이하다는 이점이 있습니다.

}

// 추가 기능 구현
private void validateCarNames(String inputName) {
int MAX_NAME_LENGTH = 5;
Copy link

Choose a reason for hiding this comment

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

자바에서 상수(불변하는 객체 또는 원시 타입) 는 항상 static final 로 클래스 맨위에 선언합니다!

레퍼런스

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다! 자료 참고 최대한 많이 하겠습니다!

String input = Console.readLine();
try {
carNames = Arrays.asList(input.split(",", -1));
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들은 많이 알면 알수록 도움이 됩니다!

Comment on lines +9 to +13
public Car(String inputName) {
validateCarNames(inputName);
this.name = inputName;
this.position = 0;
}
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.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants