Skip to content

[2단계 - 자동차경주] 이지현 미션 제출합니다. #121

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

Merged
merged 6 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@
4. Java Style Guide를 기반으로 자바 코드 컨벤션을 지킴
5. indent depth는 2를 넘지 않는다 -> 함수로 분리
6. 3항 연산자, else 예약어 금지
7. 함수의 길이는 15라인을 넘어가지 않는다. -> 한 가지 일만 하도록 구현
7. 함수의 길이는 15라인을 넘어가지 않는다. -> 한 가지 일만 하도록 구현

## 2단계 : 우승 자동차 구하기
요구사항
1. n대의 자동차가 참여할 수 있다
2. 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다
3. 자동차 경주 게임을 완료한 후 누가 우승했는지 구할 수 있다. 우승자는 1명 이상일 수 있다.
12 changes: 6 additions & 6 deletions src/main/java/Car.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@ public class Car {
private static final int MOVE_THRESHOLD = 4;


private String carName;
private int carPosition = 0;
private String name;
private int position = 0;
private final MoveCondition moveCondition;

public Car(String carName, MoveCondition moveCondition) {
if (carName == null || carName.trim().isEmpty()) {
throw new IllegalArgumentException("차의 이름이 비어있습니다");
}
this.carName = carName;
this.name = carName;
this.moveCondition = moveCondition;
}

public int getPosition() {
return carPosition;
return position;
}

public String getName() {
return carName;
return name;
}

public void move(int value){
if (moveCondition.isMovable(value)) {
carPosition++;
position++;
}
}
}
Expand Down
76 changes: 76 additions & 0 deletions src/main/java/CarWinner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import java.util.ArrayList;

public class CarWinner {
private Car[] cars; //참가한 자동차들

Choose a reason for hiding this comment

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

java에서는 collection 사용이 권장되곤 하는데요? collection은 어떤 장점이 있을까요?

키워드 : jcf

Copy link
Author

Choose a reason for hiding this comment

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

처음에 JCF가 어떤 약자인지 몰라서 헷갈렸는데, Java Collections Framework였군요!
List는 크기가 가변적인 데이터에 좋고, 인터페이스 기반의 설계가 되어있어 코드를 유연하게 작성할 수 있습니다!
배열보다는 List를 사용해야겠네요!

Copy link

@Choi-JJunho Choi-JJunho May 8, 2025

Choose a reason for hiding this comment

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

추가적으로 list에서 제공해주는 기능이 편리하다는 부분도 하나의 큰 장점으로 다가오기도 할 것 같아요 ㅎㅎ

+) List로 반영이 되지 않은 것 같은데 확인 부탁드려요~!

Comment on lines +3 to +4

Choose a reason for hiding this comment

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

컬렉션을 Collection을 감싸면서 그 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고 합니다!
Cars를 일급컬렉션으로 한번 만들어보세요! 이 경우 어떤 식으로 테스트가 작성될 수 있는지도 같이 말씀해주세요 :)

Copy link
Author

Choose a reason for hiding this comment

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

Cars

  • 자동차 이동 (moveAll)
  • 우승자 자동차 목록 (getWinners)
  • 우승자 이름 목록 (getWinnerNames)
  • 우승자 수 (getWinnerCount)

로 일급컬렉션을 구현했습니다.

테스트에서는 Cars를 주입한 뒤, moveAll()과 getWinnerCount() / getWinnerNames()를 호출하는 방식으로
각각의 테스트 조건(n명의 우승자가 나오는지 여부)을 파라미터 기반으로 검증할 수 있게 구성했습니다.

private int winnerPosition; //우승 자동차의 위치(최대값)
private int winnerCnt; //우승 자동차의 수

Choose a reason for hiding this comment

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

cnt 와 같이 축약된 변수명은 지양해보는게 어떨까요~?

Copy link
Author

Choose a reason for hiding this comment

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

넵 수정하겠습니다!!

Choose a reason for hiding this comment

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

+) 이 부분 반영이 안된거같아요~!

private ArrayList<String> winnerNames; //우승 자동차의 이름

Choose a reason for hiding this comment

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

List로 타입을 선언하는것과 ArrayList로 타입을 선언하는 것 어떤 차이가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

//1
private ArrayList<String> arrayList = new ArrayList<>();
//2
private List<String> list = new ArrayList<>();

List는 인터페이스이고 ArrayList는 그 구현 클래스입니다.
아무래도 유연성과 유지보수 측면에서는 2번 방식이 더 권장된다고 하네요.
코딩테스트 연습할 때 ArrayList로 바로 선언하는 게 손에 익어서 자연스럽게 쓰게 되었네요 😅😅

단점으로는 List로 선언했을 때 ArrayList의 고유 메서드(ensureCapacity() 등)를 직접 사용할 수 없다는 점이 있지만,
필요할 경우 캐스팅으로 해결할 수 있으니, 인터페이스 타입을 사용하는 것이 더 바람직한 선택 같네요!

Choose a reason for hiding this comment

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

우승 자동차의 위치를 알고싶다면 어떻게 할 수 있을까요?
winner는 꼭 객체의 상태로 존재해야하는 값일까요?

Copy link
Author

@izzy80 izzy80 May 8, 2025

Choose a reason for hiding this comment

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

답변

일급컬렉션을 사용한다면 변수로 따로 저장하지 않고 메소드에서 뽑아내면 됩니다.

추가 질문

일급컬렉션에 대해 알아보다가
image
https://f-lab.kr/insight/understanding-and-applying-first-class-collections
이 글을 보고 궁금한 점이 생겼습니다.

winner는 꼭 객체의 상태로 존재해야하는 값일까요?

이 질문에 지금 요구사항에서는 결과를 저장하지 않아서 Winner 객체가 굳이 필요없는 걸까요?
그렇다면 추후 라운드별 결과를 저장하라는 요구사항이 생긴다면 Winner 객체가 생겨도 될 것 같다고 봤습니다.

그런데 여기서 약간 고민이 되는 건,
그 winner 정보를 Cars 안에 두는 게 맞는지,
아니면 따로 Winner 또는 GameResult 같은 객체를 만들어 분리해야 하는지
입니다.
약간 두 객체의 정보가 비슷한 것 같다고 생각이 되어서...

그리고 뭔가 수정한 1급 객체인 cars가 책임이 아직도 섞여있다는 생각이 들고
Cars가 경기 전의 자동차들을 담는 '초기 상태' 개체인데,
이 객체에서 경기를 실행하고, 결과(우승자)까지 직접 계산해서 내보내는 구조가
입력/실행/결과 저장의 과정이 cars에 있는게 맞나? 라는 생각이 들고 그러면 또 winner를 만들어야 할 것 같은데....그럼 질문이 도돌이표로 돌아오고....
일급컬렉션에 대한 감이 확실히 안 오다 보니 머가 맞는 지 모르겠네요

Choose a reason for hiding this comment

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

정말 공감되는 고민거리네요 ㅎㅎㅎㅎ

저도 이렇게 누구의 책임이지..? 하는 고민이 자주 드는 것 같아요
이럴때는 현실세계를 기준으로 시뮬레이션을 한번 돌려보곤 합니다
이번 자동차 미션의 경우 F1 경기장에서 자동차 경주를 보고있다는 생각을 해보게 되네요 (저는 F1에 대해서 하나도 모르지만요!)

image

저는 이 경기를 보는 관중의 입장에서 자동차 경주가 어떻게 진행되고, 우승자는 누가 결정해주지? 와 같은 고민을 하게 될 것 같아요!
그 사이에서 객체의 행위를 중심으로 책임을 가르게 되는것 같아요 😄
지현님은 어떻게 생각하시나요?

+) 살짝 부끄럽지만 약 3년전에 제가 자판기라는 미션을 했을 때 생각했던 사고의 흐름을 참고차 남겨봅니다
juno의 자판기 회고 글


public CarWinner(Car[] cars) {
this.cars = cars;
winnerCnt = 0;
winnerPosition = Integer.MIN_VALUE;
winnerNames = new ArrayList<>();
}

public int getWinnerCnt() {
return winnerCnt;
}

public int getWinnerPosition() {
return winnerPosition;
}

public ArrayList<String> getWinnerName() {
return winnerNames;
}

public void whichWinner(int testRounds){
//1. 모든 자동차를 주어진 횟수만큼 이동
moveAllCars(testRounds);
//2. 우승자의 수와 자동차의 위치(최대값) 계산
countWinners();
//3. 우승자 이름 저장
saveWinnerName();
}
Comment on lines +28 to +35

Choose a reason for hiding this comment

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

whichWinner 라는 메소드명은 어떤 의미를 나타내나요?

현재 메소드는 어떤 역할을 수행하고있나요?

Copy link
Author

Choose a reason for hiding this comment

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

whichWinner는 어떤 자동차가 우승하는지를 구하는 메소드명인데 너무 함축해서 메소드명을 쓴 것 같네요.
해당 메소드가 자동차들 이동 -> 우승자 결정 -> 우승자 정보 저장의 과정이 있어서 좀 더 포괄하는 메소드를 만들어야겠네요
메소드명이 항상 고민입니다ㅠ


//1. 모든 자동차를 주어진 횟수만큼 이동
private void moveAllCars(int testRounds){
for (Car car : cars) {
for (int i = 0; i < testRounds; i++) {
car.move(RandomUtil.randomGenerator());
}
}
}

//2. 우승자의 수와 자동차의 위치(최대값) 계산
private void countWinners(){
for (Car car : cars) {
updateWinnerCount(car);
}
}

//2-1. 자동차 한 대씩 우승자의 수와 최고 위치 갱신
private void updateWinnerCount(Car car) {
int position = car.getPosition();
if (position > winnerPosition) {
winnerCnt = 1;
winnerPosition = position;
return;
}
if (position == winnerPosition) {
winnerCnt++;
}
}

//3. 우승자 이름 저장
private void saveWinnerName(){
for (Car car : cars) {
if (car.getPosition() == winnerPosition) {
winnerNames.add(car.getName());
}
}
}
}


49 changes: 49 additions & 0 deletions src/main/java/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Cars {
//자동차 경주에 참가하는 자동차들의 집합
private List<Car> cars;

public Cars(List<Car> cars) {
this.cars = cars;
}

//자동차 이동
public void moveAll(int testRounds) {
for (Car car : cars) {
IntStream.range(0, testRounds)
.forEach(i -> car.move(RandomUtil.randomGenerator()));
}
}

//이동된 자동차들의 최대위치 구하기
private int getMaxPosition() {
return cars.stream()
.mapToInt(Car::getPosition)
.max()
.orElse(0);
}

//우승 자동차 목록
public List<Car> getWinners() {
int max = getMaxPosition();
return cars.stream()
.filter(car -> car.getPosition() == max)
.collect(Collectors.toList());
}

//우승 자동차 이름 목록
public List<String> getWinnerNames() {
return getWinners().stream()
.map(Car::getName)
.collect(Collectors.toList());
}

//우승 자동차의 수
public int getWinnerCount() {
return getWinners().size();
}

}
25 changes: 25 additions & 0 deletions src/test/java/CarTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
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.junit.jupiter.api.Assertions.*;

Expand Down Expand Up @@ -56,6 +60,27 @@ void carMultipleMove() {
assertThat(car.getPosition()).isEqualTo(2);
}

@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
@DisplayName("주어진 횟수 동안 n대의 자동차 중에 우승자가 m명 이상 나온다")
void whichCarsIsWin(int value) {
Car carA = new Car("A",moveCondition);
Car carB = new Car("B",moveCondition);
Car carC = new Car("C",moveCondition);

Cars cars = new Cars(List.of(carA, carB, carC));

int testRounds = 1;
cars.moveAll(testRounds);

//우승자는 n명 이상 나온다
assertThat(cars.getWinnerCount()).isGreaterThanOrEqualTo(value);
//우승자의 수와 이름의 수가 같아야 한다
assertThat(cars.getWinnerCount()).isEqualTo(cars.getWinnerNames().size());
}




}

Expand Down