Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

[1] 숫자 야구 게임 구현#1

Open
deok-beom wants to merge 7 commits intoCODE-CLEANERS:mainfrom
deok-beom:level_2_baseball
Open

[1] 숫자 야구 게임 구현#1
deok-beom wants to merge 7 commits intoCODE-CLEANERS:mainfrom
deok-beom:level_2_baseball

Conversation

@deok-beom
Copy link

No description provided.

import java.util.Map;
import java.util.stream.IntStream;

public class Discrimination {

Choose a reason for hiding this comment

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

class 이름을 discrimination으로 설정하신 이유가 궁금합니다!

Copy link
Author

Choose a reason for hiding this comment

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

두 숫자를 비교하는 행위를 '판별한다(discriminate)'고 표현하고 싶었습니다.
검증하다(Verify)나 판단하다(Judge)로 표현하는 게 더 자연스러웠을지 모르겠네요..

Discrimianation은 생성되는 순간 두 숫자(Number)를 판별하고 그 정보를 저장해놓았다가 표시하는 역할을 하는 클래스입니다.

}
}
}

Choose a reason for hiding this comment

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

proceed와 askPlayEnd를 BaseballGame이나 다른 클래스에서 구현 후 호출하는 방향은 어떤지 생각이 듭니다!

Copy link
Author

Choose a reason for hiding this comment

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

해당 메소드를 어떤 클래스로 호출하는 게 더 적절할지 생각해보았으나... 역시 게임의 메뉴가 이 역할을 수행하는 게 자연스럽다고 생각하여 메소드의 위치를 옮기지는 않았습니다.

대신 Main이라는 클래스 이름을 BaseballGame이라는 이름으로 변경하였습니다. (원래 BaseballGame이라는 이름을 가진 스테이지 역할의 클래스는 BaseballStage로 변경했습니다)

private final List<Discrimination> history = new ArrayList<>();

public void setNewGame() {
Set<Integer> bucket = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
Copy link
Collaborator

Choose a reason for hiding this comment

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

List.of() 스태틱 매서드를 사용해보면 어떨까요?

Comment on lines 59 to 61
public List<Discrimination> getHistory() {
return new ArrayList<>(this.history);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

사용하지 않는 매서드는 없애주는 것이 깔끔할 것 같아요!


private int pickNumber(Set<Integer> bucket) {
while (true) {
int random = (int) (Math.random() * 9) + 1;
Copy link
Collaborator

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.

이 방식이 숫자를 담은 리스트를 셔플하는 것보다 수행시간이 더 빠를 수 있겠다고 얘기를 했었는데.. 다시 생각해보니 치명적인 문제가 있었습니다. 바로 random 함수에서 꺼낸 값이 중복되면 중복될 수록 수행 시간이 더 길어질 수 있다는 것이었습니다.
그래서 셔플하는 방법으로 변경했습니다!

Comment on lines 12 to 34
public void setNewGame() {
Set<Integer> bucket = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));

int num = 0;
int digit = 1;
for (int i = 0; i < 3; i++) {
num += pickNumber(bucket) * digit;
digit *= 10;
}

this.targetNumber = new Number(num);
this.history.clear();
}

private int pickNumber(Set<Integer> bucket) {
while (true) {
int random = (int) (Math.random() * 9) + 1;
if (bucket.contains(random)) {
bucket.remove(random);
return random;
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

랜덤한 세자리수의 생성을 위해 해당 방식을 선택한 이유가 궁금해요! 궁금한점은

  1. Set을 사용한 이유
  2. setNewGame() 매서드에서 history를 clear하는 이유 (아마도 history 객체는 사용하지 않는 것 같아요)

더불어 Number 객체에게 더 많은 일을 시키게 된다면 어떤 모습이 될지도 궁금해요!

Comment on lines 36 to 38
protected void setTargetNumber(Number targetNumber) {
this.targetNumber = targetNumber;
}
Copy link
Collaborator

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 22 to 32
for (int i = 0; i < baseArr.length; i++) {
if (compareMap.containsKey(baseArr[i])) {
if (compareMap.get(baseArr[i]) == i) {
strike++;
} else {
ball++;
}

compareMap.remove(baseArr[i]);
}
}
Copy link
Collaborator

@ca1af ca1af Nov 4, 2023

Choose a reason for hiding this comment

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

해당 로직에서 else는 필요치 않은 것 같아요!

더불어 새로운 게임이 생성되고 새로운 비교(Descrimination)을 할 때마다 해당 객체가 생성되는 것 같아요! 이 방법 대신 변수들(strike, ball...)을 초기화시켜주는 방법을 사용하는 것은 어떨까요?

Comment on lines +8 to +13
public Number(int number) {
for (int i = 2; i >= 0; i--) {
nums[i] = number % 10;
number = number / 10;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 매서드는 사용하는 측에서의 활용을 모른다면(사용하는 측의 로직을 모른다면) 이해하기 힘든 로직인 것 같아요!

  1. 해당 로직의 책임을 다른 곳으로 위임하거나
  2. 해당 생성자 매서드 안에 있는 로직에 "이름을 부여" 해서 의도를 밝혀보는

것은 어떨까요?


import static org.assertj.core.api.Assertions.assertThat;

class BaseBallTest {
Copy link
Collaborator

Choose a reason for hiding this comment

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

중복되는 로직을 (주로 초기화 로직을) 줄여보면 어떨까요?

Comment on lines 62 to 63
BaseballGame baseballGame = new BaseballGame();
baseballGame.setTargetNumber(new Number(Integer.parseInt(targetNumber)));
Copy link
Collaborator

Choose a reason for hiding this comment

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

매서드의 해당 부분만을 보면 baseBallGame 은 오직 세터로직으로만 초기화되는것으로 보여요. (물론 그렇지 않음을 알지만)

해당 게임 객체의 초기화를 위한 다른 방법은 없을까요?

@deok-beom deok-beom changed the title [0] 숫자 야구 게임 구현 [1] 숫자 야구 게임 구현 Nov 8, 2023
return true;
}

@Override

Choose a reason for hiding this comment

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

hashCode() 와 equals(Object obj) 를 오버라이드한 이유가 궁금합니다...!
Number클래스는 뭔갈 상속받고있는 클래스도 아닌것 같은데.. 왜 사용하신건가요..?

Copy link
Author

@deok-beom deok-beom Nov 14, 2023

Choose a reason for hiding this comment

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

Object는 Java의 모든 클래스의 최고 조상 클래스입니다.
equals()hashCode()는 모두 이 Object가 가지고 있는 메소드입니다.

equals() 메소드는 두 인스턴스가 같은지를 비교하는 메소드로 재정의 하지 않는다면 두 인스턴스가 저장된 메모리 주소를 비교하게 됩니다. 이를 재정의 하고 있는 이유는 두 Number 인스턴스가 같은지 비교할 때 멤버 변수로 저장하고 있는 정수 배열(int[] nums) 안의 정수 값들과 그 순서로 비교하기 위해서 입니다. 이로써 실제로 같은 숫자를 저장하고 있는 Number의 인스턴스를 여러개 만들어도 메모리에 저장된 위치만 다를 뿐 같은 데이터라고 인식할 수 있습니다.

hashCode()는 보통 equals()를 재정의 할 때 함께 재정의 하는 것이 권장되고 있습니다. 이 메소드는 인스턴스의 해시코드를 구해서 반환하는 메소드로 재정의 하지 않는다면 인스턴스가 저장된 메모리 주소로 해시코드를 만들어 반환합니다. 위의 equals() 메소드에서처럼 이 메소드의 실질적인 데이터의 본질을 말하고 있는 건 int[] nums안의 정수 값들입니다. 따라서 hashCode()도 재정의하고 있습니다. (이 함수는 HashMap이나 HashSet과 같은 자료형에 저장될 때 사용됩니다.)

Copy link
Author

Choose a reason for hiding this comment

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

equals(), hashCode() 두 메소드는 해시 코드를 이용하는 컬렉션에서 주로 활용됩니다.

해시 알고리즘은 모든 인스턴스에 대해서 완전히 중복되지 않는 고유한 값을 만들어주지는 않습니다. 알고리즘에 따라서 그 확률은 다르겠지만 해시 코드값이 중복되는 경우도 있습니다.

해시 코드를 이용하는 컬렉션들은 값을 저장, 조회할 때 일단 해시코드를 이용해서 저장하고, 만약 중복되는 해시코드 값이 이미 있다면 그 이후에는 equals()를 이용해서 값이 같은지 비교하여 처리하는 방식입니다.

assertThat(result.getBall()).isEqualTo(1);
assertThat(result.getStrike()).isEqualTo(0);
assertThat(result.getOut()).isEqualTo(2);
}

Choose a reason for hiding this comment

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

@CsvSource 어노테이션을 처음봐서 검색해봤는데 입력값에 따라 결과값이 다른 경우를 테스트할 때 쓴다고 하더라구요.. 그럼 135 이 부분이 입력값이고 218 이 부분이 결과값을 미리 어노테이션으로 정의해놓은 건가요?
어떻게 적용되는것인지 궁금합니다..!

Copy link
Author

Choose a reason for hiding this comment

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

Csv는 사실 쉼표(comma ,)로 여러 데이터를 구분하고 있는 텍스트 데이터를 말합니다. 예를 들어서 135, 218 처럼...

@CsvSource는 이런 Csv의 개념을 가져와서, 서로 다른 케이스의 Csv 데이터를 이용해서 테스트를 반복 수행하도록 돕는 어노테이션입니다.

어노테이션의 value={} 속성에는 실제 수행될 여러 케이스의 Csv 데이터들이 들어갑니다.
delimiter= 속성에는 Csv 데이터 안에서 데이터들을 구분할 때 사용합니다.

구분된 데이터는 순서대로 테스트 메소드의 인자로 들어가게 됩니다.
제가 구현한 테스트 메소드에서는 "135:218" Csv 데이터라면 ':'를 기준으로 135와 218이 순서대로 메소드의 targetNumber, input 인자로 들어오게 됩니다.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants