🫡 구현 시 집중한 부분
1. 함수형 프로그래밍
함수형 프로그래밍이란, 작업을 어떻게 수행할 것인지(즉 로직에 집중하는 것이) 아니라 무엇을 할 것인지에 집중하는 방법
구체적인 작업 방식은 라이브러리가 알아서 처리하고, 사용자는 라이브러리가 제공하는 인터페이스를 구현하는 것만으로도 원하는 작업을 수행할 수 있다.
자바에서는 익명 클래스, 함수형 인터페이스, 람다, 스트림API 등의 문법을 사용할 수 있다.
선언형 프로그래밍과 명령어 프로그래밍의 차이점을 이론으로만 들었을 때는 잘 와닺지 않았었는데,
For문을 사용하여 명령형으로 사용한 코드와 Stream을 사용하여 선언형으로 작성한 코드를 비교해보니 차이점을 확 느낄 수 있었다.
함수형 인터페이스와 스트림 API를 최대한 활용하고자 하였다.👍
이번 미션에서 느낀 함수형 인터페이스의 장점은 view를 테스트 할 수 있다는 것이었다.
함수형 인터페이스를 사용하기 전에는 view에 대한 테스트를 하지 않았었는데, 위 코드처럼 사용함으로써 사용자 입력에 대한 부분도 테스트 가능하게 되었다.
2. 객체 간 의존성 제거
추가된 요구사항 중 "3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다." 라는 부분이 있었다.
따라서 최대한 하나의 객체가 아는 것 줄이고 책임을 분리하고자 노력했다.
3. 객체 간 중복 코드 제거
추가된 요구사항 중 "딜러와 플레이어에서 발생하는 중복 코드를 제거해야 한다."라는 부분이 있었다.
딜러와 플레이어는 "카드 뽑기"라는 동일한 책임을 수행하지만 뽑는 로직은 다르다.
딜러는 16 미만인 경우 반드시 하나를 뽑고 16이상이 된 순간 턴을 멈춘다.
반면, 플레이어는 21이 넘지 않는 이상, 사용자가 카드를 뽑을지 말지를 선택할 수 있다.
카드를 뽑는 행동 등 Dealer 가 특수한 형태의 Player 라고 생각을 했다.
따라서 딜러가 플레이어를 상속받게 하여 중복된 코드를 제거하였다.
이 부분에 대한 내용은 코드 리뷰를 받으면서, 잘못된 사용이었다는 것을 깨달았는데, 해당 내용은 아래 포스팅을 참고하면 된다.
👥 짝(페어)과의 공유한 내용
1. 클래스 초기화 블록
클래스 초기화 블록은 클래스가 처음 메모리에 로딩될 때 단 한번 실행되는 블록으로, 객체를 생성하지 않고도 실행이 된다.
public class Deck {
static {
makeDeck();
}
private static void makeDeck() {
// 생략
}
// ...
}
미션에서 52개의 카드 뭉치를 나타내는 Deck을 구현했는데,
하나의 Deck만을 필요로 하며 게임 실행 시 52개의 각 카드를 생성하고 랜덤하게 섞어야 한다.
때문에 클래스 초기화 블록을 이용하여 Deck을 구현하였다.
2. Collectors.collectingAndThen
Collectors.collectingAndThen()는 Collecting을 진행한 결과를 바탕으로 메서드를 추가로 진행할 수 있게 해주는 메서드이다.
public static Participants of(final List<String> rawNames, final List<Hand> cards) {
// 검증 로직 생략
return IntStream.range(0, rawNames.size())
.mapToObj(index -> new Player(new Name(rawNames.get(index)), cards.get(index)))
.collect(Collectors.collectingAndThen(Collectors.toList(), Participants::new));
}
위 코드처럼 Player 원소들을 List로 묶은 후, 바로 일급 컬렉션인 Participants라는 객체를 생성할 수 있다.
3. ScoreDisplay 과한 것이 아닌가?
구현 중 enum을 사용한 도메인들이 있었다.
처음에는 enum에서 출력 문구를 가지고 있게 했었는데, 이 부분과 관련해 논의점이 생겼었다.
예시로 요구사항에 따라 결과로 카드의 정보를 출력해야 했었는데, 현재는 A, 2, K 등처럼 출력이 주어졌지만
view에 대한 출력이 Ace, Two, King 변경된다면 Domain인 Score가 변경되어 버리는 문제였다.
따라서 Domain Enum에서 출력 문구를 관리하는 것이 아닌, 페어는 Domain과 매칭되는 View Enum을 생성하여 view와 domain간의 결합을 끊어 싶어하였다.
이렇게 View용 enum을 생성하게 되면, 확실히 앞에 나온 도메인이 출력 요구사항 때문에 변경되는 경우는 사라진다.
하지만, 관리해야 할 파일 수가 늘어난다.
또 enum의 경우는 개인적으로 치밀하게 분리를 해도 되지 않을 것 같다고 느꼈기 때문에 과한 분리라고 생각되었지만, 극한으로 분리를 해보는 것도 좋은 경험이 될 것 같아 분리를 해보았다.
4. AssertionError
자바 프로그램이 기대하는 결과와 다를 경우 발생하는 예외이다.
OutputView를 유틸리티성 클래스로 생성하면서(즉 인스턴스가 필요없는, static 메서드만을 가지고 있는 클래스) 생성자를 private로 선언했다. 인스턴스가 따로 필요없기 때문에 인스턴스 생성을 막고자 하는 의도였다.
때문에 생성자 내부에서 AssertionError를 던져 생성자가 실행될 경우 내 예상과 다른 사용임을 알리고자 했다.
📖 미션을 진행하면서 학습한 것들
1. List의 copyOf(), of() 메서드
그동안 List를 사용할 때 of() 메서드를 많이 사용했었는데, 이번 블랙잭을 하면서 copyOf() 메서드를 사용했었다.
두 메서드의 차이점에 대해 내가 정리한 글을 첨부한다.
https://mincanit.tistory.com/57
2. 요다 컨디션
요다 컨디션에 대해 들어본 적이 있으신가요?
이번 블랙잭을 하면서 요다 컨디션이라는 것에 대해
아래처럼 작성된 비교 조건문을 의미합니다.
if("HELLO WORLD" == word) {
// ...
}
NULL 방지가 가능하지만 가독성을 조금 양보해야 하기도 한다.
'우아한테크코스 6기 > 1단계' 카테고리의 다른 글
[블랙잭 미션] 피드백 및 코드 리뷰 (0) | 2024.04.22 |
---|---|
[블랙잭 미션] 감정 회고 (2) | 2024.04.13 |
[사다리 타기 미션] 학습 내용 정리 (0) | 2024.03.26 |
[자동차 경주 미션] 학습 내용 정리 (1) | 2024.03.25 |
일급 컬렉션 (1) | 2024.03.25 |