진짜.. 블로그 글 왤케 밀린걸까..
바쁘다고는 생각하지만 그정도까지는 아닌거같은데 ...ㅎ
(열심히 삽시다 제발 ~)
블랙잭 미션은 앞의 미션과 비슷하게, 객체지향의 원칙을 적용하도록 요구했다.
1단계
2단계
companion object {
private val ALL_CARDS: MutableSet<Card> = CardNumber.values()
.flatMap { cardNumber ->
listOf(
Card(cardNumber, Shape.CLOVER),
Card(cardNumber, Shape.HEART),
Card(cardNumber, Shape.DIAMOND),
Card(cardNumber, Shape.SPADE)
)
}.toMutableSet()
fun draw(): Card {
val card = ALL_CARDS.random()
ALL_CARDS.remove(card)
return card
}
}
변경 가능한 컬렉션을 static하게 선언해두는 것은 좋지 않습니다.
ALL_CARDS가 모두 remove되는 경우에 이 프로그램은 더 이상 정상적으로 동작하지 않을거에요.
모든 카드를 불변으로 캐시해두는 것과 그 카드들을 하나씩 draw 하는 기능을 별도로 분리해보세요.
진짜.. 왜 저렇게 했는지 기억이 안나지만.. companion object 내부에 변경 가능한 변수를 선언해두었다 ;; companion object
ALL_CARDS를 Set 컬렉션을 바꿔주고, 직접 remove() 하지 않도록 코드를 변경하였다. 추가적으로, CardPack 클래스를 생성하여, 그 내부에서 draw()를 통해 카드를 뽑도록 코드를 수정하였다.
private val ALL_CARDS: Set<Card> = CardNumber.values()
.flatMap { cardNumber ->
Shape.values().map {shape ->
Card(cardNumber, shape)
}
}.toSet()
enum class Shape(val description: String) {
HEART("하트"),
CLOVER("클로버"),
SPADE("스페이드"),
DIAMOND("다이아몬드")
}
UI 로직입니다. UI layer에서 데이터를 받아서 그리도록 만들어보세요.
이 미션에서 ui에 사용하는 문자열들을 죄다 enum 클래스에 적어두었었다... 그래서 같은 리뷰를 3개나 받음 ㅎㅎ..
enum class Shape {
HEART, CLOVER, SPADE, DIAMOND
}
// ui
private fun makeShapeDescription(shape: Shape): String {
return when (shape) {
Shape.HEART -> HEART_DESCRIPTION
Shape.DIAMOND -> DIAMOND_DESCRIPTION
Shape.CLOVER -> CLOVER_DESCRIPTION
Shape.SPADE -> SPADE_DESCRIPTION
}
}
그래서 enum class를 다음과 같이 재정의하고, ui에서 string으로 바꿔주는 로직을 추가해주었다.
테스트 본문에는 테스트하고자 하는 것을 이해할 수 있는 정보를 모두 가지고 있어야 하고, 반대로 불필요한 내용을 전부 감추어야 합니다.
읽기 좋은 테스트의 조건에는 clarity(명확성), resilience(탄력성), completeness and conciseness(완전성과 간결성)이 있다.
리뷰어님은 완전성과 간결성에 대해 말씀하셨다.
❕ 테스트는 이해하는데 필요한 모든 정보가 포함됨과 동시에, 불필요한 정보들을 포함하지 않고 있을 때 완전하면서 간결해진다. 생성자에 포함된 많은 산만한 정보들은 메서드로 따로 분리하여 세부 정보를 숨겨야 완전하고 간결한 테스트를 작성할 수 있다.
자세한 예시는 너무 길어서 내가 작성한 프롤로그를 첨부한다 !
읽기 좋은 테스트
fun draw(): Card {
val card = cards.shuffled()[0]
cards.removeAt(0)
if (cards.size == 0) {
cards.addAll(Card.ALL_CARDS.toList())
}
return card
}
직접 0번째 카드를 가져오는 대신 LinkedList와 같이 이미 잘 만들어진 자료구조를 활용해보는건 어떨까요?
리뷰어님이 내가 뻘짓 하는 모습을 못참으셨는지 LinkedList를 사용하라고 직접 언급해주셨다..ㅎㅎ
왜 pop 같은 기능이 없을까 ~ 라고 생각했는데 이미 있었다는거 ㅎㅎ^^:;
fun draw(): Card {
val card = cards.removeFirst()
if (cards.size == 0) {
cards.addAll(Card.ALL_CARDS)
cards.shuffle()
}
return card
}
Linked List에 대해서는 컴공이라면, 학교에서 기본적으로 배웠을거라 생각하고 짧게 정리해본다.
메모리 공간에 값이 한 번에 나열되어 있는 Array와 달리,
Linked List는 각 요소 내에 다음 요소를 가리키는 Pointer가 존재하여 연결되는 구조이다. 즉, 메모리 공간에 모여있지 않고 산재되어질 수 있다(non-continuous). Kotlin에서는 Reference를 통해 서로를 연결한다.
연결리스트에서는 추가 및 삭제가 자유롭지만 대신 탐색은 느리다.
반면 배열에서는 탐색은 빠르지만, 추가 및 삭제는 메모리 이동이 필요하므로 느리다. 이를 이용해서 적절히 사용할 때를 잘 구분하자 ~.~
시간이 너무 지나서 작성하다보니 당연한게 된 리뷰들이 많아서.. 조금 많이 건너 뛰었다.. 리뷰 피드백은 바로바로 작성하도록 하자 🥲
우기랑.. 페어한것도 기억이 안난다.. ㅋㅋㅋㅋㅋㅋ 나름.. 내가 치근덕거리며 잘 지냈다고 생각했는데 ㅎ.. 정신 차리고 살자 ~