[우아한테크코스 백엔드 4기] 레벨1 - "블랙잭" 회고

도박은 정신건강에 해롭다.

🔗 관련 링크

깃허브 링크
작성한 코드


⚙️ 구현 기능 목록

입력

  • 참여자 이름 입력 받는 기능
    • [예외] 빈 문자열을 입력받은 경우
    • [예외] 문자열 앞뒤에 구분자가 있는 경우
  • 참여자의 배팅금액 입력 받는 기능
    • [예외] 숫자가 아닌 경우
  • 한장 더 받는지 여부 확인 기능
    • [예외] y 또는 n이 아닌 문자를 입력받은 경우

블랙잭 게임 진행

  • 카드묶음 생성
  • 카드 생성
  • 플레이어 모음 객체 생성
  • 플레이어 객체 생성
    • [예외] 중복된 이름이 입력된 경우
  • 숫자, 모양 이넘객체 생성
  • 배팅금액 객체 생성
    • [예외] 0 이하일 경우
  • 이름 객체 생성
    • [예외] 쉼표 기준으로 입력받지 않을 경우
  • 딜러, 참여자에게 두 장의 카드를 지급
  • 카드 숫자 계산
    • 21을 초과 시 패배
    • Ace는 1 또는 11로 계산
    • King, Queen, Jack은 각각 10으로 계산
  • 카드 추가 기능
    • 참여자 : 숫자를 합쳐 21 미만이면 한장 더 받는지 묻기
    • 딜러 : 2장의 합계가 16 이하이면 반드시 1장의 카드 추가
  • 승패 계산 기능
    • 카드 숫자를 합쳐 21을 초과하지 않거나 21에 가깝게 만들면 이긴다
    • 플레이어 첫 두장이 블랙잭
      • 딜러 20이하/버스트 : 플레이어 승리, 배팅금액 1.5배
      • 딜러 블랙잭 : 무승부
    • 딜러가 버스트
      • 플레이어 모두 승리
    • 딜러가 21 이하
      • 플레이어 21 이하 : 더 높은 점수인 쪽이 승리
      • 플레이어 21 초과 : 딜러 승리
  • 최종 돈 반환 기능

출력

  • 카드 목록 및 점수 출력
  • 최종 딴 금액 결과 출력

⛳ 피드백

@사이드 피드백

1.컨벤션

  • 상수와 인스턴스 변수 사이 개행
  • 패키지 이름은 소문자로 구성되어야 함
  • Test 코드 사용시 @DisplayName 사용하는 것으로 통일
    -테스트메서드 (영어) 이름도 함께 신경쓰자
  • 테스트코드의 메서드 간 개행
    - 테스트를 위한 준비문, 단언문 사이 개행

2.코드 정리

  • 사용하지 않는 import 문은 정리
  • 문서(README.md)에서 유지보수 고려해 커밋
    - 체크되지 않은 TODO는 커밋서 제외하기
  • 테스트코드도 유지보수성, 가독성 고려하기

3.InputView 리턴값은 원시값인 편이 좋다!

  • 되도록 View가 도메인을 모르게 하자

@출력에 필요한 기능은 View에서 처리

  • 콘솔에 표시되기 위한(입출력용) Delemiter는 view 안에서 명명, 처리
    - 필요시 DTO 사용
  • toString(), HashCode()는 본 용도에 맞게
    - 두 메서드를 View 출력 용도로 쓰지 말자!!(꼼수ㄴㄴ)

@테스트코드 용 로직은 Fixtures 클래스에서

  • 기존 Domain 등에서 테스트 코드에서만 사용되는 구현(메서드, 생성자...) 존재
    - 테스트를 위한 구현...? 문제적이다
    • 테스트 디렉토리 내에 전용 클래스(Fixtures)를 만들어주자!
  • 전략패턴을 쓸 때는?
    - 예외적으로 값을 주입받기 위한 서브클래스를 만들 수는 있다
    • 하지만 원칙적으로는 문제가 있는듯...
    • 되도록 Fixtures를 사용하는거로 생각 중
      (계속 공부하고, 고민하자!)

@싱글턴

  • 만드는 법
    1. public static final 필드 방식 - bad..?(리플렉션으로 깨짐)
    2. 정적 팩터리 방식 - soso
    3. Enum 방식- good!
  • 장점
    - 여러번 호출할 땐, 메모리 이득 시간 이득
  • 단점
    - 개방 폐쇄 원칙 위반
    - 테스트코드 작성이 힘들다

@Comparator는 신중히 쓰자

public boolean isBust() {
    return getPoint().compareTo(BLACKJACK_POINT) > 0;
}
  • 이해하기 위한 인지적 부하가 더 크다
  • 포인트 계산 로직 자체를 해당 클래스 안에 넣은 후 private getter를 쓰자

@Stack, Deque

  • 처음엔 스택을 사용
  • 스택은 후입선출, 큐는 선입선출
  • 해당 케이스의 경우 큐(Deque)가 더 맞다
public class Application {
    public static void main(String[] args) {
        Deque<String> deque = new ArrayDeque<>();
        deque.addFirst("첫 번째 요소"); // "첫 번째 요소"
        deque.add("두 번째 요소"); // "첫 번째 요소", "두 번째 요소"
        deque.push("세 번째 요소"); // "세 번째 요소", "첫 번째 요소", "두 번째 요소"
        System.out.println(deque.pop());
        System.out.println(deque.pop());
    }
}
// 실행 결과
// > Task :Application.main()
// 세 번째 요소
// 첫 번째 요소
  • 이때, 대부분의 상황에서는 DequeLinkedList 보다 ArrayDeque 을 사용

@동일성과 동등성?

  • 동일성 : 같은 객체인가?
  • 동등성 : 객체 내부 값이 같은가?(동등한가)

🖋 소감

몰아치는 피드백에 부족함을 깨달았다

블랙잭 페어 직후 땐 기본 구현이 일찍 끝나 좋아했다.
그런데 알고보니 기본 룰 규칙 숙지에 있어 미흡한 부분도 있었고,
스스로 정한 목표들을 지키지 못한 부분들도 있었다.

제대로 질문하자

  1. 질문은 구체적으로
  2. 질문은 관련 코드에 커멘트로 달기
  3. ~~해당 방향이 맞나요? 보다는 내 생각(근거)를 먼저 말하고 의견 묻기
  4. 필요시 DM 등으로 추가질문 하기

질문하는 법에 대해서도 깨달은 지점이 있다.
단순히 내가 모르는 부분을 물어보면, 상대방은 알아서 대답해줄 거라 생각했다.
하지만 리뷰어도 나와같은 사람이다!
애매하고 이해하기 힘든 질문을 쏟아내면 이를 해독하는 리뷰어 입장이 난처해질 수 있다.

앞으로 질문할 때 위 네 방법을 고려해야겠다.

끊임없이 의심하자

수업에서, 여러 책들과 리뷰어의 피드백에서 여러 개념들을 배웠다.
공부해가며 느끼는건 정답은 없다는 것이다.

누군가는 정적팩토리메서드를 최고의 방법론으로 떠받치지만,
다른 이는 문서화가 힘들고, 생성자를 쓰는 편이 낫다고 말한다.(심지어 객체지향적이지 않다고도 한다...)

누군가는 abstract를 통한 상속은 이후 템플릿 메소드 패턴을 통해 안정적으로 사용할 수 있다고 한다.
또다른 누군가는 그럼에도 높은 결합도에 문제가 있으며,
되도록 조합과 인터페이스 상속을 이용하라 권장한다.

최근 책 오브젝트 스터디에 참여하고 있다.
책에 마법의 단어 "트레이드 오프"가 자주 등장한다!
책임을 (독자에게) 위임하는 저자..?😂

결국 코드에 정답은 없다.
구현 요구사항과 명백한 몇 규칙은 지켜야겠지만^^
그 안의 세부적인 구현 방식은 아티클 몇개에 의존하지 않고 스스로 판단해 결정해야겠다!

단단하게 공부하자

  1. 배운 것을 직접 기록하자(복붙 X, 내 언어로!)
  2. 코드에 적용해본 후 피드백을 받자
  3. 다른 크루들과 말로 배움을 공유하자(배우고 가르쳐주고 토론하기)

학습로그나 블로그, 노션 기록 등을 통해
나름 열심히 머릿속에 집어넣었다고 생각했다.
막상 이러한 구현을 왜 했는지, 그 근거들을 되짚다 보면 애매하게 알고있던 부분을 발견하게 된다.

아예 모르면 공부하지만, 애매하게 알면 답이 없다. 남들과 소통할 때에도 삐걱거릴 수밖에 없다.
보다 단단하게 공부하자!🔥

좋은 웹페이지 즐겨찾기