[🚀 사이클1 - 미션 (블랙잭 게임 실행)] 제제 미션 제출합니다#1015
[🚀 사이클1 - 미션 (블랙잭 게임 실행)] 제제 미션 제출합니다#1015alstj2384 wants to merge 69 commits intowoowacourse:alstj2384from
Conversation
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
Co-authored-by: alstj2384 <ass91160@naver.com>
| import java.util.List; | ||
|
|
||
| public class BlackjackGame { | ||
| private final List<Player> players; |
There was a problem hiding this comment.
해당 클래스에는 "게임 실행 흐름을 관리하는 책임"을 부여했으나, "참여 인원 관리"라는 책임도 떠맡고 있다는 생각도 듭니다.
players와 dealer를 참여 인원을 관리하는 클래스에서 관리하도록 책임을 분리하면 BlackjackGame의 인스턴스 변수 개수도 2개로 줄어들 것이라고 기대되는데, 적절한 분리라고 생각할 수 있을까요?
There was a problem hiding this comment.
BlackjackGame 이라는 이름으로 보아 참여인원 관리도 어색하진 않다고 생각해요
단순 players 의 관리 때문이라면 Player 컬렉션에 대한 일급 컬렉션을 만들어봐도 좋겠네요
이 경우는 복잡성에 대한 트레이드 오프도 고려해볼 수 있겠네요!
src/main/java/view/ResultView.java
Outdated
| System.out.println(participant.getName() + "카드: " + participant + " - 결과: " + participant.getTotalSum()); | ||
| } | ||
|
|
||
| public void printResultStatistics(List<Player> players, Dealer dealer) { |
There was a problem hiding this comment.
도메인에서 메서드 라인 10줄 이하, 깊이 2 초과 금지 와 같은 규칙을 준수하는 것은 필요하다고 생각합니다만, 출력을 담당하는 클래스에서도 이를 완벽히 준수해야 할까요?
클래스 내부의 여러 함수들이 중복으로 사용하는 코드는 메서드로 분리하는 게 적절하다고 생각하지만,
출력을 위해 단순 일회성으로 사용되기 위한 코드들의 분리를 고려하며 시간을 쓰는 것이 적절한가? 라는 생각을 해봤습니다.
리뷰어님의 의견이 궁금합니다!
There was a problem hiding this comment.
출력을 담당하는 코드도 현재는 관리하는 코드라고 생각하긴해요!
관리하는 코드라면 유지보수를 해야하는 대상이기에 규칙을 준수하는 것이 좋다고 생각합니다 :)
UI 출력에 대한 코드를 원래라면 다룰 일이 거의 없긴한데
현재는 성장하기위한 미션의 특성상 규칙을 준수하는것도 좋다고 봐요~
다만 너무 깊게 고민해야하고 규칙 준수를 위해 억지스러운 변경을 해야한다면 넘어가는게 좋다고 생각합니다.
(이런 부분은 리뷰어에게 공유가 필요할것이구요)
There was a problem hiding this comment.
좋은 의견 감사합니다!
말씀대로 우선 성장을 위해 규칙 준수를 고려하되, 너무 비용이 크다고 판단되면 넘어가고 리뷰어에게 공유하는 방향으로 진행해 보려고 합니다!
There was a problem hiding this comment.
혹시 호옥시 오해할까봐 작성합니다 ㅎㅎㅎ... 😅
넘어가도 된다고 판단하는것은 제제가 기준을 명확하게 만들고 그것을 리뷰어에게 공유해주셔야합니다!! ㅎㅎ
예를들면) view 의 구조가 규칙에 맞지않아 긴 시간 고민을 이어갔는데, 더이상 개선이 힘들것같고 도메인 개선에 더 집중하는것이 미션에서 중요하다 판단하여 넘어갔습니다!
이런 기준이 있다면 좋을 것 같아요!
| public class Deck { | ||
| private final Cards cards; | ||
|
|
||
| public Deck() { | ||
| this.cards = new Cards(); | ||
| init(); | ||
| cards.shuffle(); | ||
| } |
There was a problem hiding this comment.
Deck은 "모든 종류의 카드 생성 및 셔플이라는 역할"을 맡고 있습니다. 그런데 이 역할을 제외하면 나머지는 단순 Cards의 메서드를 래핑하는 형태라고 생각이 드는데, "모든 종류의 카드 생성 및 셔플" 역할만으로 클래스를 분리한 것이 타당하다고 생각할 수 있을까요?
There was a problem hiding this comment.
Deck 이 Cards 를 가지는 이유는 무엇일까요?
Deck 그 자체로 Card 의 일급 컬렉션으로 볼 수 있지 않을까요 🤔
제제의 의견이 궁금합니다!
There was a problem hiding this comment.
같은 도메인이어도 역할에 따라 다른 일급 컬렉션으로 구분해볼 수 있었군요. 이 부분은 고려하지 못했습니다.
저는 “플레이어가 가진 카드들”과 “덱에 들어있는 카드” 모두 “여러 개의 카드”라는 공통적인 특징이 있어서 Deck을 Cards 일급 컬렉션으로 관리하면 중복 코드가 줄어들 것이라고 기대했습니다.
생각해보면 프로젝트 규모가 커질수록 여러 역할의 일급 컬렉션을 만들어야 할 수도 있을 것 같습니다. 만약 역할에 따라 다수의 일급 컬렉션을 만들 경우, 일급 컬렉션들 사이에 공통적으로 동작하는 메서드가 많을수록 관리포인트가 늘어날 수 있다는 생각이 들었는데요!
찰리는 역할에 따라 일급 컬렉션을 만드는 부분을 어떻게 생각하시는지 궁금합니다. 그리고 일급 컬렉션 간 같은 행동을 하는 메서드가 중복되는 경우는 확장성을 위한 트레이드 오프로 봐야할까요?
의도하신 요지가 맞는지 정확히 모르겠지만, 다른 의도셨다면 말씀해 주시면 감사하겠습니다 🙂
There was a problem hiding this comment.
만약 동일한 검증이 필요하고 Deck has cards 구조가 어울린다면 그렇게 해도 될 것 같아요!
하지만 Deck 이 Cards 를 의존하면 몰라도되는 인터페이스(Cards 에서 노출되는 메서드들) 까지 알게될 거에요 :)
즉 Deck 이 Cards 일급 컬렉션을 가지는것은 과한 정보를 가진다고 생각했어요 😄
Deck 은 카드덱으로서 일급컬렉션의 가치가 있을것이고
Cards 는 참가자가 사용하는 카드패로써의 일급컬렉션 가치가 있을것같네요
생각해보면 프로젝트 규모가 커질수록 여러 역할의 일급 컬렉션을 만들어야 할 수도 있을 것 같습니다. 만약 역할에 따라 다수의 일급 컬렉션을 만들 경우, 일급 컬렉션들 사이에 공통적으로 동작하는 메서드가 많을수록 관리포인트가 늘어날 수 있다는 생각이 들었는데요!
관리 포인트는 늘어나지만 하나의 일급컬렉션이 여러 역할을 가진다면 그것대로 문제가 생기지않을까요 🤔
당연히 공통적으로 검증하거나 동작하는 부분이 많다면 하나의 일급 컬렉션을 추출하고 그것을 의존하는 여러 객체들을 만드는게 합리적이라 생각해요 :)
Gomding
left a comment
There was a problem hiding this comment.
안녕하세요 제제!
블랙잭 미션 함께하게된 찰리입니다.
미션 구현하느라 고생하셨어요 🎉
몇가지 코멘트 남겼으니 확인부탁드려요~
궁금한 점 있으면 언제든 DM 이나 코멘트 남겨주세요.
There was a problem hiding this comment.
- 현재 상황
- 빠른 기능 완성을 위해 출력 계층에서 도메인 객체를 직접 참조하고, Getter를 통해 정보를 출력했습니다.
- 고민 지점:
- 도메인 정보를 Getter로 직접 꺼내는 방식은 객체지향과 거리가 먼 방법이라는 생각이 들었습니다. DTO를 도입하더라도 결국 ‘누가 데이터를 담아줄 것인가’라는 문제가 생겼고, 외부에서 담아주려면 결국 Getter를 써야하고, 도메인이 직접 DTO를 반환하게 하자니 출력만을 위한 로직이 도메인에 침투하는 느낌을 받았습니다.
- 질문
- 이런 상황에서 도메인의 순수성을 지키면서도 효율적으로 출력을 처리하는 적절한 방법이 있을까요?
- 리뷰어님께서는 보통 이 트레이드오프를 어떤 기준으로 결정하시나요?
Getter 를 쓰는것은 왜 안될까요? 🤔
결국 도메인의 정보를 어디가는 사용해야하지 않을까요?
이런 상황에서 도메인의 순수성을 지키면서도 효율적으로 출력을 처리하는 적절한 방법이 있을까요?
도메인의 순수성이란 무엇일까요? getter 를 사용하면 항상 지켜질 수 없는 걸까요?
리뷰어님께서는 보통 이 트레이드오프를 어떤 기준으로 결정하시나요?
제제가 생각하는 트레이드 오프가 어떤것인지 이해못했어요 😢
트레이드 오프라는 것은 여러 방향에 장단점이 있고 그것을 저울질 하는것인데
현재 제제가 생각하는 도메인외에 다른것을 출력계층에 노출하는 것의 장단점도 공유해주시면 좋겠어요
There was a problem hiding this comment.
Getter 를 쓰는것은 왜 안될까요? 🤔
결국 도메인의 정보를 어디가는 사용해야하지 않을까요?
Getter를 사용하면 해당 값을 사용하는 메서드는 Getter의 자료형에 의존하게 되어서 필드 자료형 변경 시 변경 범위가 넓어지는 점 때문에 주저했던 것 같습니다.
말씀대로 결국 어딘가에서 정보를 사용해야 하니 출력을 위한 Getter를 사용하는 것은 어쩔 수 없다고 생각이 들었습니다.
도메인의 순수성이란 무엇일까요? getter 를 사용하면 항상 지켜질 수 없는 걸까요?
제가 도메인 순수성이라는 단어를 정확히 이해하지 못하고 사용했던 거 같습니다. 도메인 순수성의 명칭을 알아보니, 도메인에서 다른 계층과 상호작용하지 않도록 하는 것으로, getter 사용과는 무관한 단어라는 생각이 들었습니다.
제제가 생각하는 트레이드 오프가 어떤것인지 이해못했어요 😢
트레이드 오프라는 것은 여러 방향에 장단점이 있고 그것을 저울질 하는것인데
현재 제제가 생각하는 도메인외에 다른것을 출력계층에 노출하는 것의 장단점도 공유해주시면 좋겠어요
제가 트레이드 오프로 생각했던 점은 "출력을 위한 getter 사용은 반드시 필요할까? 다른 방법이 있을까?" 였습니다. 단어를 잘못 사용한 부분이 있네요!
There was a problem hiding this comment.
Getter 로 노출을 하는게 좋다는것은 아니지만
어쩔 수 없이 사용해야한다고 생각해요 ㅎㅎ..
제제의 고민은 당연히 이해하고 충분히 좋은 고민이라 생각해요!
도메인 내부에 있는것을 최대한 보호하고 싶다. 캡슐화 하고싶다. 외부에서 값을 가져가서 마음대로 쓰는게 싫다. 등등의 의미가 있으니까요.
하지만 객체가 협력해서 만든 결과를 어딘가는 사용해야하기 때문에 getter 로 값을 가져가는것은 어떤 측면에서는 자연스럽다고 생각해요.
그리고 도메인을 view 까지 노출했을 때 어떤 단점이 있을까? 라는점도 고민해볼 수 있는 포인트 입니다 😄
There was a problem hiding this comment.
Controller 의 로직이 매우 간단한것만 봐도 블랙잭 게임 흐름을 도메인 내부에 최대한 구현한것이 느껴지네요 💯 💯
There was a problem hiding this comment.
TDD 적용과 설계 수준에 대한 고민
1. 현재 상황
- 초기 간단한 도메인 객체는 TDD로 접근이 가능했으나, 게임 흐름을 관리하는 종합적인 설계 단계에서는 TDD를 적용하는 데 어려움을 겪었습니다.
2. 고민 지점
- 설계가 미숙한 상태에서 TDD를 시작하니 테스트 범위를 어느 정도로 나누어야 할지 막막했습니다. 세밀하게 나누자니, 설계 능력이 부족하고 시간이 오래 걸릴 것 같았습니다. 또한 “빠르게 구현하라”라는 가이드와는 거리가 먼 방법이라고 생각이 들었습니다.
- 질문
- 리뷰어님께서는 TDD를 적용하기 전에 설계를 어느정도 수준까지 하는 것이 적절하다고 생각하시나요?
경험이 없다면 설계는 어려운게 당연합니다 :)
TDD 는 완벽한 설계에서 시작하는것이 아니라고 생각해요
오히려 TDD 를 하면서 설계를 발견하는 경우가 많습니다.
현재는 도메인 구현을 위한 도메인 모델, 요구사항을 정리해보고, 필요한 도메인 객체들을 도출한 뒤 시작해보는것으로 충분하다고 생각해요
빠르게 구현하라 -> TDD를 실행하면 테스트를 통과하는 최소한의 구조로 구현을 하게되요. 이는 빠르게 구현하라와 이어진다고 생각합니다 ㅎㅎ 일단 테스트를 통과하는 동작하는 코드가 만들어지는거니까요
아래 영상도 추천드릴게요!
There was a problem hiding this comment.
제가 TDD를 너무 무겁게 생각하려는 경향이 있었네요. 조금 더 가벼운 마음으로 수행해 보겠습니다!
개발을 두 가지 모드로 진행한다는 관점이 새로우면서 적용하면 좋을 거 같아 리팩토링 과정에서 의식적으로 인지하며 수행해 보았습니다!
나머지 내용은 약간 이해는 가지만 아직 와닿지는 않아 천천히 텀을 두고 다시 한 번씩 봐야겠습니다 😢
좋은 영상과 의견 감사합니다!
There was a problem hiding this comment.
와닿지 않는게 당연할수도 있어요!
일부만 이해해도 충분히 훌륭합니다 👍
| public List<Player> getPlayers() { | ||
| return players; | ||
| } | ||
|
|
||
| public Dealer getDealer() { | ||
| return dealer; | ||
| } |
There was a problem hiding this comment.
getter 같은 경우는 다른 메서드들보다 중요도가 떨어지니
클래스 최하위에 놔두는것이 좋다고 생각해요 :)
중간에 있으면 가독성에 방해가 되는경우가 있습니다 ㅎㅎ
There was a problem hiding this comment.
좋은 의견 감사합니다!
코드를 모르는 사람 입장에선 클래스에서 외부에 노출되는 메서드(핵심기능)가 먼저 보이는게 좋겠다는 생각도 드네요!
관련해서 메서드 정렬 기준을 "접근 지정자"로 할지, "논리적 단위"로 할지 고민이 생겼었는데요
처음에는 "논리적 단위"로 묶어서 접근 지정자 상관 없이 묶어두었습니다.
다만 위 관점(핵심 로직이 먼저 보이는 구조)으로 생각한다면, private 메서드는 메서드 이름 자체로 행위를 유추할 수 있으므로 굳이 public과 묶어두지 않아도 될 거 같다는 생각도 들었습니다.
찰리는 어떻게 생각하시나요? 찰리만의 기준도 여쭙고 싶습니다!
There was a problem hiding this comment.
https://dodop-blog.tistory.com/m/277
해당 글에 있는 순서를 따라갑니다!
private 메서드 같은 경우 provate 메서드를 호출하는 메서드 바로 아래쪽에 놔두는 편입니다 ㅎㅎ
가장 가까운곳에서 찾을 수 있도록요
아래로 읽어가는게 자연스러우니..
단점은 아래쪽에서 다른 메서드가 동일한 private 호출하면 스크롤을 위로 올려서 봐야한다는 점 😅
닉네임 중복 검사 로직 추가 플레이어에게 카드를 추가하는 로직을 함수형 인터페이스로 구현
| public void add(Supplier<Card> getCard) { | ||
| cards.add(getCard.get()); | ||
| } | ||
|
|
||
| public void addAll(List<Card> cards) { | ||
| this.cards.addAll(cards); | ||
| } | ||
|
|
||
| public void addAll(Function<Integer, List<Card>> getCardsFunc, int quantity) { | ||
| cards.addAll(getCardsFunc.apply(quantity)); | ||
| } |
There was a problem hiding this comment.
Card를 추가하는 방법으로 함수형 인터페이스도 지원하도록 수정해 봤는데요!
다양한 입력을 자체적으로 지원하는 것이 유연한 도메인이라는 생각이 들어서 이렇게 구성해 봤습니다.
리팩토링을 마치고 프로젝트 전체 코드를 다시 훑어보니, 함수형 인터페이스를 조금은 무분별하게 사용한 것이 아닌가? 라는 느낌을 받았습니다. 함수형 인터페이스에 대한 감각이나 지식이 부족한 탓에 "함수형 인터페이스는 언제 적용하는 게 적절할까?", "함수형 인터페이스는 필요할 때만 사용해야 하나?" 와 같은 저만의 적절한 기준을 아직 세우지 못했습니다.
실무 관점에서 함수형 인터페이스는 주로 어떤 상황에 주로 사용하고, 사용 선호도(지양하는 편인가, 추천하는 편인가, 상황에 따라 다른가)에 대해서 여쭙고 싶습니다!
There was a problem hiding this comment.
함수형 인터페이스 어려운게 맞습니다 ㅎ..
바깥에서 행위를 주입받아 유연하게 처리하는 상황에 많이 사용하는데요
재사용성, 확장성을 높여주는 장점이 있는데
Java 에서 대표적인것은 stream api 가 있어요 :)
필요할때만 쓰는게 좋다고 생각해요
오용하면 오히려 가독성이 안좋아지고
메서드 내부에서 해야할 행위가 비깥에 노출될 수 있습니다
여러번 사용해보는것도 좋은 학습이라 생각해요!
그리고 어디에 사용했을때 적절했는지 고민해보는거죠 :)
물론 현재는 거의 사용할 일이 없다고 생각해요!😅
| public void giveHand() { | ||
| players.giveCardsToEachPlayers((quantity) -> deck.pullCards(quantity), DEFAULT_HAND_NUMBER); | ||
| dealer.addCards((quantity) -> deck.pullCards(quantity), DEFAULT_HAND_NUMBER); | ||
| } |
There was a problem hiding this comment.
두 코드 모두 (quantity) -> deck.pullCards(quantity) 람다식이 사용되고 있습니다.
만약 해당 람다식을 클래스 더 많은 곳에서 사용한다면 해당 함수형 인터페이스도 클래스의 변수로 따로 관리해 볼 수 있겠다고 생각이 들었는데요!
자주 사용되는 함수형 인터페이스를 변수로 관리하는 것에 대한 의견이 궁금합니다!
There was a problem hiding this comment.
이전 코멘트에서도 말씀드렸지만
필요할때만 쓰는게 좋다고 생각해요
오용하면 오히려 가독성이 안좋아지고
메서드 내부에서 해야할 행위가 비깥에 노출될 수 있습니다
이런 상황은 아닐지 고민해보면 좋겠어요! 😳
당연히 자주 사용되는 함수형 인터페이스라면 상수로 만들어서 재사용하는것도 방법입니다.
다만.. 함수형 인터페이스를 여러곳에서 재사용하는 상황은 흔하지 않으므로 경계해보면 좋겠어요 🤗
| public void giveCardsToEachPlayers(Function<Integer, List<Card>> getCard, int quantity) { | ||
| for (Player player : players) { | ||
| player.addCards(getCard.apply(quantity)); | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
함수형 인터페이스를 사용하지 않고, 아래와 같이 구현할 수도 있을 거 같은데요.
public void giveCardsToEachPlayers(Deck deck, int quantity) {
for (Player player : players) {
player.addCards(deck.pullCards(quantity));
}
}이 경우 Players에 Deck에 대한 의존성이 생기게 됩니다.
구현할 당시에는 "불필요한 의존성은 최대한 줄이자" 라는 생각이 들어서 함수형 인터페이스를 적용해 봤는데요!
지금 생각해 보면, 꼭 그렇게 의존성을 줄여야만 할까? 라는 생각도 듭니다. 아직 의존성에 대한 판단 기준이 명확하지 않아서 그런 것 같습니다.
Players에서 Deck을 의존하는 것은 타당하다고 볼 수 있을까요?
의존성이 생겨도 되는 경우를 어떻게 판단해볼 수 있을까요?
There was a problem hiding this comment.
Player가 Deck을 가지는것은 플레이어가 덱의 기능을 사용할 수 있음을 의미하는데
그것이 타당하고 자연스럽다면 문제 없다고 생각해요 :)
불필요한 의존이 생기는것은 어떤 문제를 발생시킬까요?
의존한다는것은 무엇을 의미할까요~
예를들어 player가 deck을 의존한다면
deck의 코드를 변경하면 player는 어떻게 될까요?
| public List<Player> getPlayers() { | ||
| return List.copyOf(players); | ||
| } |
There was a problem hiding this comment.
현재 copyOf는 기존 List와 연결을 끊고 새로운 불변 리스트를 작성해 줍니다.
다만 내부 객체의 참조는 그대로 이어져서 다음 행동은 수행될 수 있습니다.
List<Player> returnPlayers = players.getPlayers();
returnPlayers.get(0).add(new Card(10));이 경우 객체의 정보가 의도치 않게 수정될 수 있는 여지가 생길 거 같은데요.
여지를 없애기 위해 깊은 복사를 수행하는 게 좋을까요?
getter는 출력단에서만 사용하기로 합의했다면 이 정도는 넘어가도 괜찮을까요?
There was a problem hiding this comment.
이정도면 넘어가도 되겠지가 항상 좋지않은 결과를 불러올수 있어요 🥲
깊은복사를 수행하거나 player가 가진 cards도 불변하게 반환해주는 방법도 있겠군요 :)
Gomding
left a comment
There was a problem hiding this comment.
안녕하세요 제제!
고민과 함께 답변 남겨주셨네요~
의견과 함께 추가로 몇가지 코멘트 남겼으니 확인부탁드려요
궁금한 점 있으면 언제든 DM 이나 코멘트 남겨주세요
There was a problem hiding this comment.
Getter 로 노출을 하는게 좋다는것은 아니지만
어쩔 수 없이 사용해야한다고 생각해요 ㅎㅎ..
제제의 고민은 당연히 이해하고 충분히 좋은 고민이라 생각해요!
도메인 내부에 있는것을 최대한 보호하고 싶다. 캡슐화 하고싶다. 외부에서 값을 가져가서 마음대로 쓰는게 싫다. 등등의 의미가 있으니까요.
하지만 객체가 협력해서 만든 결과를 어딘가는 사용해야하기 때문에 getter 로 값을 가져가는것은 어떤 측면에서는 자연스럽다고 생각해요.
그리고 도메인을 view 까지 노출했을 때 어떤 단점이 있을까? 라는점도 고민해볼 수 있는 포인트 입니다 😄
src/main/java/view/ResultView.java
Outdated
| System.out.println(participant.getName() + "카드: " + participant + " - 결과: " + participant.getTotalSum()); | ||
| } | ||
|
|
||
| public void printResultStatistics(List<Player> players, Dealer dealer) { |
There was a problem hiding this comment.
혹시 호옥시 오해할까봐 작성합니다 ㅎㅎㅎ... 😅
넘어가도 된다고 판단하는것은 제제가 기준을 명확하게 만들고 그것을 리뷰어에게 공유해주셔야합니다!! ㅎㅎ
예를들면) view 의 구조가 규칙에 맞지않아 긴 시간 고민을 이어갔는데, 더이상 개선이 힘들것같고 도메인 개선에 더 집중하는것이 미션에서 중요하다 판단하여 넘어갔습니다!
이런 기준이 있다면 좋을 것 같아요!
| public class Deck { | ||
| private final Cards cards; | ||
|
|
||
| public Deck() { | ||
| this.cards = new Cards(); | ||
| init(); | ||
| cards.shuffle(); | ||
| } |
There was a problem hiding this comment.
만약 동일한 검증이 필요하고 Deck has cards 구조가 어울린다면 그렇게 해도 될 것 같아요!
하지만 Deck 이 Cards 를 의존하면 몰라도되는 인터페이스(Cards 에서 노출되는 메서드들) 까지 알게될 거에요 :)
즉 Deck 이 Cards 일급 컬렉션을 가지는것은 과한 정보를 가진다고 생각했어요 😄
Deck 은 카드덱으로서 일급컬렉션의 가치가 있을것이고
Cards 는 참가자가 사용하는 카드패로써의 일급컬렉션 가치가 있을것같네요
생각해보면 프로젝트 규모가 커질수록 여러 역할의 일급 컬렉션을 만들어야 할 수도 있을 것 같습니다. 만약 역할에 따라 다수의 일급 컬렉션을 만들 경우, 일급 컬렉션들 사이에 공통적으로 동작하는 메서드가 많을수록 관리포인트가 늘어날 수 있다는 생각이 들었는데요!
관리 포인트는 늘어나지만 하나의 일급컬렉션이 여러 역할을 가진다면 그것대로 문제가 생기지않을까요 🤔
당연히 공통적으로 검증하거나 동작하는 부분이 많다면 하나의 일급 컬렉션을 추출하고 그것을 의존하는 여러 객체들을 만드는게 합리적이라 생각해요 :)
There was a problem hiding this comment.
와닿지 않는게 당연할수도 있어요!
일부만 이해해도 충분히 훌륭합니다 👍
| @@ -0,0 +1,12 @@ | |||
| package domain; | |||
|
|
|||
| public class Constant { | |||
There was a problem hiding this comment.
깊이 고민해주셨네요 👍👍
여러 클래스에서 쓰이는 경우가 무조건 발생하죠
그럴때는 저도 접근제한자(public, protected, 등)로 조절합니다!
여러곳에서 접근해야하는데 한 곳에 두긴 애매하다면 적절한 외부 클래스를 만들어볼 수도 있구요!
후자의 경우는 거의 경험해본적이 없긴합니다 ㅎㅎ
정말 공통으로 많이 쓰는 상수 (예를들면 정말 범용적인 에러메시지, 에러코드) 같은 경우는 Constant 같은 성격의 클래스를 민들어볼 수 있다고 생각해요
하지만 이것도 시간이 지날수록 여러 상수가 쌓이면 관리가 어렵긴해요 😅
하나의 클래스에 모여있는 상수들은 관계가 있는 클래스나 사용하고있는곳에 위치하도록 수정해보시면 좋겠어요~
| import java.util.function.Supplier; | ||
|
|
||
| public class Cards { | ||
| private final List<Card> cards; |
There was a problem hiding this comment.
카드를 뽑을때는 어느 한방향에서 순차적으로 뽑게되는데
List 말고 더 어울리는 컬렉션 유형은 없을까요? 😃
| public Players(List<String> players) { | ||
| this(); | ||
| validate(players); | ||
| addPlayers(players); |
There was a problem hiding this comment.
필드와 관련없는 인자를 받는다면 정적 팩토리 메서드도 고려해보면 좋겠어요 :)
그리고 this() 를 먼저 호출하지않고 List 를 생성한 후에
this.players 에 바로 초기화하는 방법도 고민해보면 좋겠어요~
| public void addCards(Function<Integer, List<Card>> getCardsFunc, int quantity) { | ||
| cards.addAll(getCardsFunc, quantity); | ||
| } |
There was a problem hiding this comment.
이 부분도 함수형 인터페이스를 사용해야할 이유가 있는지 고민해보시면 좋겠어요~
| @@ -0,0 +1,19 @@ | |||
| package domain; | |||
|
|
|||
| public enum ExceptionMessage { | |||
There was a problem hiding this comment.
Constant 와 동일합니다 :)
사용하는곳에 위치해보고 이전과 비교해보시면 좋겠어요!
| private int getSumWithoutAce() { | ||
| int sum = 0; | ||
| for (Card card : cards) { | ||
| sum += card.getRankValueIfNotAce(); | ||
| } | ||
| return sum; | ||
| } |
There was a problem hiding this comment.
이미 Score 라는 포장된 객체가 있는데 int 값을 가져와서 계산하고 있군요 🤔
한번 Score 라는 객체를 활용하도록 구현해보시면 좋겠어요~
아래는 힌트인데 꼭 해당 코드를 그대로 사용할 필요는 없어요!
private Score getSumWithoutAce() {
Score total = 0;
for (Card card : cards) {
total.sum(card.getScore())
}
return total;
}
안녕하세요. 블랙잭 게임 미션 제출합니다! 😄
첫 미션인 만큼, 미흡한 점이 많아 페어와 많은 고민을 나누느라 주말에 제출하게 되었습니다 😢
체크 리스트
test를 실행했을 때, 모든 테스트가 정상적으로 통과했나요?어떤 부분에 집중하여 리뷰해야 할까요?
설계 및 구현 과정에서 들었던 질문을 정리해 보았습니다!
출력 계층에서 도메인을 직접 사용하는 것이 적절한가?
도메인 로직과 입출력 흐름 제어의 분리에 대한 고민
TDD 적용과 설계 수준에 대한 고민
1. 현재 상황
2. 고민 지점
코드 관련 질문은 코멘트로 달아두었습니다!
감사합니다.