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

[1단계 - 자동차 경주 구현] dino.shin(신종화) 미션 제출합니다. #13

Merged
merged 24 commits into from
Mar 27, 2024

Conversation

jjongwa
Copy link

@jjongwa jjongwa commented Mar 26, 2024

안녕하세요! 이번 카카오 기술 온보딩 진행중인 디노라고 합니다! 🦖

이번 미션에서는 의식적으로 최소 단위를 찾아내는 것과
전체적인 구현 과정에 대해 tdd 사이클을 지키는 것을 중점적으로 두었습니다!

Name, Position, StepCount 등 원시값 포장을 통해 각각의 상태와 제약 조건을 관리하도록 구성했고,
객체의 리스트를 대상으로 진행되는 메서드들은 Cars와 Winners와 같은 일급 컬렉션을 만들어 책임을 부여해 주었습니다.

또한, 0~9 사이의 값을 랜덤으로 받아 진행하는 부분의 테스트는 NumberGenerator라는 인터페이스를 두고
production과 test에서 사용하는 구현체를 각각 구성해 가능한 최소단위의 테스트들을 모두 작성하려 했습니다!

이번 미션의 테스트를 진행하면서 여러 가지 메서드들을 활용해보려 했는데요,
한가지 궁금한 점이 생겨 질문 드립니다..!

Q. assertAll과 assertSoftly 사용의 기준

제가 현재 알고 있는 assertAll과 assertSoftly의 차이는 softly에서는 실패한 검증문의 위치를 결과에 바로 알려준다 입니다.
하지만 이러한 경우 무조건 softly가 all보다 좋아 굳이 all을 쓸 필요가 없지 않을까 하는 생각이 들었는데,
혹시 이 두 방식의 또 다른 차이점이 있는지, 리뷰어님은 어떤 기준에 따라 softly와 all을 쓰시는지 궁금합니다!

이번 미션 잘 부탁드려요!🙇🙇🙇

jjongwa and others added 19 commits March 25, 2024 14:46
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Copy link

@hyunssooo hyunssooo left a comment

Choose a reason for hiding this comment

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

안녕하세요. 🙂
이번 미션 함께 진행할 정현수입니다.
몇 가지 코멘트 남겼어요. 확인 부탁드립니다!

Comment on lines 13 to 14

private final static Calculator CALCULATOR = new Calculator();

Choose a reason for hiding this comment

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

Suggested change
private final static Calculator CALCULATOR = new Calculator();
private static final Calculator CALCULATOR = new Calculator();

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 18 to 19

final Matcher m = Pattern.compile(CUSTOM_REGEX).matcher(text);

Choose a reason for hiding this comment

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

Pattern.compile(CUSTOM_REGEX) 이 부분은 한번 생성하고 재사용 가능하지 않을까요?
이펙티브 자바 아이템 6. 불필요한 객체 생성을 피하라 를 읽어보시면 좋을 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

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

말씀해 주신대로 Patten을 재사용할 수 있게 변경했습니다.! a1a9f11

Comment on lines +28 to +30

private Integer getSum(final String[] inputNumbers) {
final List<Integer> numbers = Arrays.stream(inputNumbers)

Choose a reason for hiding this comment

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

return intInteger 는 어떤 차이가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

Integer는 원시값 타입인 int를 래핑하여 추가적인 함수를 제공하고,
null 값을 처리할 수 있는 것으로 알고 있습니다!

스트림을 통해 List로 변환하는 과정에서 파싱된 String 값 중 null이 있는지 체크하는 과정이 필요할까요?

Choose a reason for hiding this comment

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

큰 의미가 있는 코멘트는 아니였어요. Calculator#calculateCalculator#getSum 리턴 타입 차이가 있어 남긴 코멘트였습니다!

이펙티브 자바 아이템 61. 박싱된 기본 타입보다는 기본 타입을 사용하라
가볍게 읽어보셔도 좋을 것 같아요!

Comment on lines +5 to +7

public class CustomNumberGenerator implements NumberGenerator{

Choose a reason for hiding this comment

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

테스트용 구현체 👍

Comment on lines +2 to +4

public class Name {

Choose a reason for hiding this comment

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

원시값 포장 👍
불변 객체로 잘 작성해 주셨습니다.

String name1 = new Name("a");
String name2 = new Name("a");

두 객체는 같다고 볼 수 있을까요? Value Object에 대해서 학습해 보면 좋을 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

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

각각 다른 인스턴스에 해당하기 때문에 다르게 인식됩니다!
VO에 해당하는 객체들에 equals & hashCode 재정의 하는 걸 놓쳤네요..

해당 작업 진행 & 테스트에서 usingRecursiveComparison() 사용하던 테스트 변경헀습니다! d97e22c

Comment on lines 26 to 29

public static boolean isMove(final CarAction targetCarAction) {
return targetCarAction.equals(MOVE);
}

Choose a reason for hiding this comment

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

Enum 의 인스턴스도 객체인데 각 객체에게 위임할 수 있지 않을까요? 여기서 static 메서드가 필요할까요?

Copy link
Author

Choose a reason for hiding this comment

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

확실히 해당 객체에게 역할을 부여해 주는게 더 자연스러워 보이네요.!

변경했습니다! 8e029d6

Comment on lines +7 to +9

public class Cars {

Choose a reason for hiding this comment

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

일급컬렉션 👍

Comment on lines 33 to 42
public Winners getWinners() {
return new Winners(values.stream()
.max(Comparator.comparingInt(car -> car.getPosition().getValue()))
.map(car -> values.stream()
.filter(c -> c.getPosition().getValue() == car.getPosition().getValue())
.map(Car::getName)
.collect(Collectors.toUnmodifiableList()))
.orElseThrow(() -> new IllegalArgumentException(INVALID_CARS_STATE_MESSAGE)));
}

Choose a reason for hiding this comment

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

car.getPosition().getValue() 를 통해서 값을 꺼내서 비교하고 있는데요. 객체 자체로 비교하거나 객체에게 메시지를 보내보는 것은 어떨까요?

Car의 Position을 통해서 비교하고 있는데 Position 자체를 비교할 수 있는 방법은 없을까요? 더 나아가 Car끼리 비교할 수 있는 방법은 없을까요??

Copy link
Author

Choose a reason for hiding this comment

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

compareTo를 재정의 하여 포지션끼리의 비교를 해당 객체 자체가 맡을 수 있도록 변경하였고,
Car 끼리의 비교도 isAt 메서드를 통해 비교하도록 변경해 봤습니다!

또한, 우승자를 도출하는 메서드가 이중 스트림으로 구성되어 있어 이를 findWinnerPosition()과 getWinners()로 분리하는 작업도 추가적으로 진행해 보았습니다! 4bdee22

jjongwa and others added 5 commits March 27, 2024 14:26
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
Co-authored-by: zeroberry <98147817+zeroberry@users.noreply.github.com>
@jjongwa
Copy link
Author

jjongwa commented Mar 27, 2024

@hyunssooo 꼼꼼한 리뷰 감사합니다! 🙇🙇

특히 Cars에서 우승자 도출 부분은 생각하지 못했는데,
아무래도 스트림이 완전히 익숙하지 않다보니 구현하면서 객체지향 원칙들을 어기게 된 것 같습니다.. 아직 갈 길이 머네요..

남겨주신 리뷰에 대한 답글 & 리팩토링 간단히 진행했습니다!

감사합니다!

+앞서 질문사항 하나 남겼는데, 이 부분도 확인해 주시면 감사하겠습니다..!

Copy link

@hyunssooo hyunssooo left a comment

Choose a reason for hiding this comment

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

안녕하세요. 🙂
몇 가지 코멘트 남겼어요.
다음 step 진행하시면서 확인 부탁드립니다!

Comment on lines +4 to +6

public class Position implements Comparable<Position>{

Choose a reason for hiding this comment

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

Comparable, equals, hashCode 👍
이 클래스도 VO로 만들기 적당한 것 같은데 어떻게 생각하시나요?

Copy link
Author

Choose a reason for hiding this comment

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

Car에서 Position을 final로 가지고 있게 하기 위해서 Position의 상태는 변화하도록 구성했었는데,

Position은 VO로 만들어 불변을 유지하고 Car가 가지고 있는 Position 필드에 final을 제거하는게 조금 더 자연스럽게 느껴지네요!
다음과 같이 변경해 봤습니다! 46e075c

Comment on lines +23 to +27
public List<String> getNames() {
return names.stream()
.map(Name::getValue)
.collect(Collectors.toUnmodifiableList());
}

Choose a reason for hiding this comment

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

일반적으로 getter는 필드 그대로를 리턴합니다.
view를 위해서라면 Name 자체가 VO이니 view에 그대로 나가도 괜찮지 않을까요?

Copy link
Author

Choose a reason for hiding this comment

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

아하 저는 도메인(model) 객체가 view에 나가는 것은 지양해야 한다.
-> VO도 도메인의 범주에 들어간다
-> VO도 view로 빠져나가 안된다

의 흐름이었는데,
불변을 유지하는 VO가 밖으로 빠져나간다 해도 크게 사이드 이펙트가 발생할 일은 없겠군요..!

그러면 이러한 VO은 DTO의 역할도 함께 포함한다고 보면 되는 걸까요?

Comment on lines +28 to +30

private Integer getSum(final String[] inputNumbers) {
final List<Integer> numbers = Arrays.stream(inputNumbers)

Choose a reason for hiding this comment

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

큰 의미가 있는 코멘트는 아니였어요. Calculator#calculateCalculator#getSum 리턴 타입 차이가 있어 남긴 코멘트였습니다!

이펙티브 자바 아이템 61. 박싱된 기본 타입보다는 기본 타입을 사용하라
가볍게 읽어보셔도 좋을 것 같아요!

Comment on lines +32 to +36
public List<CarResponse> extractCarInfos() {
return cars.getValues().stream()
.map(CarResponse::new)
.collect(Collectors.toUnmodifiableList());
}

Choose a reason for hiding this comment

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

RacingGame 자체는 도메인 모델로 보이는데 CarResponse 와 같은 DTO를 의존해도 괜찮을까요? 이 부분은 함께 고민해 보면 재밌을 주제 같습니다. 개인적으로는 도메인 모델이 DTO를 의존하는 것은 선호하지 않는데요. 의존성 방향 때문이에요. 어떻게 생각하시는지 의견 남겨주시면 감사합니다~

Copy link
Author

Choose a reason for hiding this comment

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

의존성 방향에 대해서 아주 동의합니다! 이 부분은 step2 PR에서 조금 더 의견 남기겠습니다ㅎㅎ

Comment on lines +46 to +59
void winnersTest() {
// given
final Cars cars = new Cars("lucas,dino,eve,helen");

// when
cars.step(new CustomNumberGenerator(List.of(9, 4, 2, 0)));

// then
assertAll(
() -> assertThat(cars.getWinners().getNames()).hasSize(2),
() -> assertThat(cars.getWinners().getNames()).contains("lucas"),
() -> assertThat(cars.getWinners().getNames()).contains("dino")
);
}

Choose a reason for hiding this comment

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

Cars#getWinners 는 Winners 객체를 리턴하는데 객체 비교로도 충분하지 않을까요?

        final Winners winners = new Winners(
            List.of(
                new Name("lucas"),
                new Name("dino")
            )
        );

        assertThat(cars.getWinners()).isEqualTo(winners);

해당 테스트가 통과하려면 어떻게 코드를 변경해야 할까요?

Copy link
Author

Choose a reason for hiding this comment

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

예상되는 우승자 리스트(expectedWinner)를 만들어 객체끼리 비교하는게 가독성 측면에서도 더 좋아 보이네요.!

Winner의 equals를 재정의 하는 것 보단 usingRecursiveComparison()을 사용해 검증하는 방식으로 바꿔봤는데, 현수님이 의도하신 부분이 맞는지 모르겠네요..!
아니라면 바로 다시 알려주세요! 6154fc1

Comment on lines +36 to +41
assertAll(
() -> assertThat(carsValues.get(0).getPosition()).isEqualTo(onePosition),
() -> assertThat(carsValues.get(1).getPosition()).isEqualTo(onePosition),
() -> assertThat(carsValues.get(2).getPosition()).isEqualTo(zreoPosition),
() -> assertThat(carsValues.get(3).getPosition()).isEqualTo(zreoPosition)
);

Choose a reason for hiding this comment

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

Q. assertAll과 assertSoftly 사용의 기준

제가 질문을 놓쳤네요. ㅠㅠ 저도 assertSoftly는 이번에 처음 알았네요.
말씀하신 것 처럼 softly에서는 실패한 검증문의 위치를 결과에 바로 알려준다 라면 굳이 assertAll을 사용하지 않아도 괜찮을 것 같아요! 취향 차이인 것 같습니다! 🙂

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