-
Notifications
You must be signed in to change notification settings - Fork 388
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
[2단계 - 블랙잭 베팅] 알파카(최휘용) 미션 제출합니다. #714
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 알파카! 최소한의 구현으로 요구사항을 충족하고자 하셨군요~ 👍
그러나 요구사항에 만족되지 않은 부분들이 있는 것 같아요.
README.md 파일에 요구사항을 추가로 정리해보면 좋겠습니다!
그 외 몇 가지 코멘트 남겼으니 확인해주세요.
궁금하신 점 언제든지 질문 남겨주세요~!
README.md
Outdated
- [ ] 최종 수익을 출력한다 | ||
- [ ] 이름과 수익 액수를 출력한다 | ||
- [ ] 승리하면 베팅 금액만큼 수익을 거둔다 | ||
- [ ] 패배하면 베팅 금액만큼 손해를 거둔다 | ||
- [ ] 무승부이면 최종 수익은 0이다 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀 블랙잭은 상금이 다르다는 요구사항도 있었던거 같아요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기본적인 요구사항을 지나쳐버렸네요 죄송합니다.. 🙇
BlackJackResult에서 매개변수로 점수 대신 핸드의 정보를 받도록 수정해 블랙잭인지 판단하고 결과로 반환할 수 있도록 기능을 추가했습니다!
public BettingAmount(String input) { | ||
int amount = parseAmount(input); | ||
validatePositive(amount); | ||
this.amount = amount; | ||
} | ||
|
||
private int parseAmount(String input) { | ||
try { | ||
return Integer.parseInt(input); | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException("베팅 금액은 양의 정수여야 합니다"); | ||
} | ||
} | ||
|
||
private void validatePositive(int input) { | ||
if (input <= 0) { | ||
throw new IllegalArgumentException("베팅 금액은 양의 정수여야 합니다"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
베팅 금액이 문자열로 전달된다는 것까지도 비즈니스 규칙인지 고민해볼 수 있겠네요!
정수인지 문자열인지... 까지는 view 의 책임으로도 볼 수 있겠어요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
필요한 타입으로 변경해주는 것까지 View에서 처리하는게 맞다고 생각했는데 예외 처리를 한 번에 하기 위해서 위의 코드처럼 짰던 것 같습니다.. view의 책임이 맞는 것 같아 말씀하신 것처럼 수정했습니다!
public class BettingTableTest { | ||
@Test | ||
@DisplayName("입력받은 Gamer들의 결과에 따라 최종 수익을 반환한다") | ||
void initWithMap() { | ||
BettingTable bettingTable = new BettingTable(Map.of("test", new BettingAmount("1000"))); | ||
Assertions.assertThat(bettingTable.getTotalProfit(Map.of("test", GamerResult.WIN))) | ||
.isEqualTo(Map.of( | ||
GamerIdentifier.DEALER_IDENTIFIER, -1000, | ||
"test", 1000 | ||
)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확인하고 싶은 내용은 게이머와 딜러의 상금이 반대로 간다이므로,
베팅에 참여하는 게이머 수를 늘리고, 딜러의 상금 결과가 얼마가 되는지 정확히 확인해보면 더 좋겠네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 여러 경우의 수에서도 잘 작동되는지 테스트하도록 추가해봤습니다!
package domain.constant; | ||
|
||
public final class GamerIdentifier { | ||
public static final String DEALER_IDENTIFIER = "dealer"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
딜러 표현 방식을 "dealer" 문자열이 아닌 웹 이미지로 변경하는 것도 비즈니스 규칙으로 보아야 할까요?
동료 개발자가 딜러 표현 방식을 변경하고자 할 때 domain 과 view 패키지 중 어떤 패키지를 먼저 열어보게 될지 상상해보면 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
딜러와 플레이어의 dto를 GamerDto 하나로 관리하고 싶었는데, 여기서 문제는 플레이어만 이름을 갖고 있다는 것이었습니다. 그래서 딜러와 플레이어를 구분하기 위해 일종의 식별자로 사용하고자 넣었고 출력 도메인에서 해당 식별자를 통해 원하는 출력 양식으로 출력할 수 있도록 구현했습니다. GamerDto를 재사용하고 싶어서 이렇게 구현하게 됐는데, 차라리 딜러의 dto를 분리하는 편이 더 나을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
인과관계가 역전된 느낌인데요, dto 는 계층간 소통을 위해서 데이터를 나르는 짐꾼 객체,
언제든지 추가되고 언제든지 변경되고 언제든지 삭제될 수 있는 데이터 덩어리에 불과한데,
그런 객체를 위해서 전체적인 구조에 변화가 생겨나는 걸로 보여요. -
지난 코멘트 설명했듯 코드 배치가 적당한가 아닌가를 판별할 수 있는 방법은 "나중에 코드를 열어볼 때 이 로직과 관련된 코드는 여기에 있겠지?" 라고 생각해요. 현재
GamerIdentifier
는GamerDto
를 위해서만 존재하는 것 같은데, 동료 개발자가 딜러를 이미지로 표현하고자 할 때GamerIdentifier
를 곧장 열어볼 수 있을지 상상해보면 도움이 될 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확실히 말씀을 듣고보니 주객이 전도된 느낌이 크네요. GamerDto를 딜러와 플레이어로 나눴고, 이름이 필요한지만 차이가 나므로 PlayerDto가 DealerDto를 상속하도록 했습니다.
그런데 리팩토링하면서 다시 살펴보니 BettingTable이 총 이익을 계산하는 데에도 DEALER_IDENTIFIER를 사용하고 있었습니다. 이 클래스에서는 처음 들어온 베팅액을 저장했다가 이후 결과를 받아서 총 결과를 반환하는데 딜러와 플레이어의 결과를 한 번에 반환하고 싶어서 사용하게 된 것 같습니다. 이 또한 딜러와 플레이어의 결과를 받는 메서드를 분리해서 처리했습니다!
public enum GamerResult { | ||
WIN, | ||
DRAW, | ||
LOSE; | ||
WIN(1), | ||
DRAW(0), | ||
LOSE(-1); | ||
|
||
private static final int BUST_THRESHOLD = 21; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. 딜러와 플레이어가 모두 동시에 블랙잭인 경우 플레이어는 베팅한 금액을 돌려받는다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
점수 대신 Hand의 정보를 받아 블랙잭인지 확인하고 블랙잭인 경우의 수도 고려해서 결과를 낼 수 있도록 수정했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 알파카, 요구사항 잘 구현해주셨습니다 👍
domain 패키지에도, dto 패키지에도 각각 dto 들이 존재하는 것 같은데요,
어떤 기준으로 패키지를 분리해두신 것인지 설명해주시면 코드를 이해하는데 더 큰 도움이 될 것 같아요!
질문 주신 내용과 함께 간단한 코멘트 추가로 남겼으니 확인해주세요!
궁금하신 점 질문 코멘트 남겨주세요~ 👍
README.md
Outdated
- [ ] 최종 수익을 출력한다 | ||
- [x] 이름과 수익 액수를 출력한다 | ||
- [x] 승리하면 베팅 금액만큼 수익을 거둔다 | ||
- [x] 패배하면 베팅 금액만큼 손해를 거둔다 | ||
- [ ] 블랙잭일 경우 베팅 금액의 1.5배의 수익을 거둔다 | ||
- [x] 무승부이면 최종 수익은 0이다 | ||
- [ ] 딜러와 플레이어가 둘 다 블랙잭이면 무승부이다 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☑️
@Test | ||
@DisplayName("플레이어가 여러 명인 경우") | ||
void variousPlayersWinCase() { | ||
BettingTable bettingTable = new BettingTable(Map.of( | ||
"test1", new BettingAmount(1000), | ||
"test2", new BettingAmount(2000), | ||
"test3", new BettingAmount(3000))); | ||
Assertions.assertThat(bettingTable.getTotalProfit(Map.of( | ||
"test1", GamerResult.WIN, | ||
"test2", GamerResult.WIN, | ||
"test3", GamerResult.WIN))) | ||
.isEqualTo(Map.of( | ||
GamerIdentifier.DEALER_IDENTIFIER, -6000D, | ||
"test1", 1000D, | ||
"test2", 2000D, | ||
"test3", 3000D | ||
)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
package domain.constant; | ||
|
||
public final class GamerIdentifier { | ||
public static final String DEALER_IDENTIFIER = "dealer"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
인과관계가 역전된 느낌인데요, dto 는 계층간 소통을 위해서 데이터를 나르는 짐꾼 객체,
언제든지 추가되고 언제든지 변경되고 언제든지 삭제될 수 있는 데이터 덩어리에 불과한데,
그런 객체를 위해서 전체적인 구조에 변화가 생겨나는 걸로 보여요. -
지난 코멘트 설명했듯 코드 배치가 적당한가 아닌가를 판별할 수 있는 방법은 "나중에 코드를 열어볼 때 이 로직과 관련된 코드는 여기에 있겠지?" 라고 생각해요. 현재
GamerIdentifier
는GamerDto
를 위해서만 존재하는 것 같은데, 동료 개발자가 딜러를 이미지로 표현하고자 할 때GamerIdentifier
를 곧장 열어볼 수 있을지 상상해보면 도움이 될 것 같아요!
public Map<String, Double> getTotalProfit(Map<String, GamerResult> totalResult) { | ||
validateSize(totalResult); | ||
Map<String, Double> totalProfit = new HashMap<>(); | ||
totalResult.forEach((key, value) -> | ||
totalProfit.put( | ||
key, | ||
bettingAmounts.get(key).getAmount() * value.getProfitRatio() | ||
)); | ||
totalProfit.put(GamerIdentifier.DEALER_IDENTIFIER, getDealerProfit(totalProfit)); | ||
return totalProfit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GamerResult
로부터 배율을 뽑아와서 직접 연산해줄 필요 없이, GamerResult
에게 메세지를 보내어 연산결과를 얻어낼 수도 있겠네요 😄
객체에 메세지를 보내라.
그리고 이 방식은 추후에도 getProfitRatio
를 통해 배율값이 여기저기 의존되어 배율을 변경하기 어려운(유지보수가 어려운) 상황을 방지하기도 좋을 것 같아요.
"블랙잭 우승시 배율을 4배로 늘리자!" 라는 요구사항이 전달될 때도 GameResult
의 배율값만 변경하면 유지보수를 끝낼 수 있겠어요!
src/main/java/domain/HandStatus.java
Outdated
public class HandStatus { | ||
private final int score; | ||
private final int handCount; | ||
|
||
public HandStatus(int score, int handCount) { | ||
this.score = score; | ||
this.handCount = handCount; | ||
} | ||
|
||
public int getScore() { | ||
return score; | ||
} | ||
|
||
public int getHandCount() { | ||
return handCount; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
record 로 표현할 수도 있겠네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
record는 생각 못해봤는데 훨씬 낫겠네요!
dto라고 하면 도메인의 정보를 view로 넘겨준다는 역할에만 집중해서 생각하지 못했는데 HandStatus도 dto였네요. 일관성을 위해 해당 클래스를 dto 패키지로 이동했습니다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 알파카! 블랙잭 미션 마지막까지 고생 많으셨어요.
dto 에 관련된 코멘트를 여러차례 남겨드렸던 만큼,
마지막 체스 미션을 진행하시는 동안
도메인 객체와 vo, dto, 엔티티간의 차이를 명확하게 이해하고 다뤄보는 경험을 해보시면 좋을거 같아요.
레벨2에 영속화 객체(엔티티)를 만나기 시작하면서 큰 인지부조화가 찾아올텐데,
이를 헤쳐나가기 위해선 명확한 구분과 이해가 필요할거에요. 💪
벌써 레벨1 마지막 미션을 앞두고 있네요.
체스 미션도 재밌으니 마지막까지 화이팅 하시길 바랍니다!
고생 많으셨어요!! 👍
- [x] 최종 수익을 출력한다 | ||
- [x] 이름과 수익 액수를 출력한다 | ||
- [x] 승리하면 베팅 금액만큼 수익을 거둔다 | ||
- [x] 패배하면 베팅 금액만큼 손해를 거둔다 | ||
- [x] 블랙잭일 경우 베팅 금액의 1.5배의 수익을 거둔다 | ||
- [x] 무승부이면 최종 수익은 0이다 | ||
- [x] 딜러와 플레이어가 둘 다 블랙잭이면 무승부이다 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😄 👍
안녕하세요 현구막!
2단계에서는 1단계의 구조를 수정하지 않고, 베팅을 관리하는 클래스를 하나 추가해서 처리하도록 구현했습니다.
해당 클래스는 처음 베팅 금액을 입력받아 생성되고, 이후 결과를 인자로 받아 최종 결과를 Map으로 반환하는 기능만 처리하고 있습니다.
또한 이를 위해 정수 값을 포장한 베팅 금액 클래스를 구현했습니다.
이번 2단계 미션 리뷰도 잘 부탁드립니다!
그리고 저번 1단계 때 제가 의견을 남기지 않은 리뷰가 있었습니다..
못 본 것은 아니고 비슷한 내용의 리뷰에 남긴 의견에 포함되어 있었어서 지나쳤던 것 같습니다.
해당 내용을 언급했어야 했는데 죄송합니다ㅜㅜ 열심히 하겠습니다 🫡