코틀린 디자인 패턴
생성 패턴
언제 그리고 어떻게 객체를 생성할지에 관한 디자인 패턴.
- 싱글톤 패턴
- 팩토리 메서드 패턴
- 추상 팩토리 패턴
- 빌더 패턴
- 프로토타입 패턴
싱글톤 패턴
- 시스템에 인스턴스가 딱 하나만 존재
- 프로그램이 시작되자마자 싱글톤 인스턴스가 만들어지면 안됨
- 인스턴스 생성은 필요한 첫 순간에 이뤄져야 함
- 두 스레드가 동시에 싱글톤 객체를 생성하려고 할 때 두 스레드가 같은 인스턴스를 획득해야 함
- 시스템의 모든 부분에서 인스턴스에 접근할 수 있어야 함
=> 코틀린에선 object
키워드를 사용하면 싱글톤 객체를 구현할 수 있다.
object NoMovieList : List<String> {
override fun equals(other: Any?) = other is List<*> && other.isEmpty()
override fun hashCode(): Int = 1
override fun toString(): String = "[]"
override val size = 0
override fun isEmpty(): Boolean = true
override fun contains(element: String): Boolean = false
override fun containsAll(elements: Collection<String>): Boolean = elements.isEmpty()
override fun get(index: Int): Nothing =
throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")
override fun indexOf(element: String): Int = -1
override fun iterator(): Iterator<String> = EmptyIterator
override fun lastIndexOf(element: String): Int = -1
override fun listIterator(): ListIterator<String> = EmptyIterator
override fun listIterator(index: Int): ListIterator<String> {
if (index != 0) throw java.lang.IndexOutOfBoundsException("Index: $index")
return EmptyIterator
}
override fun subList(fromIndex: Int, toIndex: Int): List<String> {
if (fromIndex == 0 && toIndex == 0) return this
throw java.lang.IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex")
}
}
internal object EmptyIterator : ListIterator<Nothing> {
override fun hasNext(): Boolean = false
override fun hasPrevious(): Boolean = false
override fun nextIndex(): Int = 0
override fun previousIndex(): Int = -1
override fun next(): Nothing = throw NoSuchElementException()
override fun previous(): Nothing = throw NoSuchElementException()
}
싱글톤 객체는 생성자를 가질 수 없다. 싱글톤 객체 초기화가 필요하다면 init 블록을 사용할 수 있다.
팩토리 메서드 패턴
객체를 생성하는 메서드에 관한 디자인 패턴.
예를 들어 체스를 보면 기물의 종류가 많이 있다. ChessPiecs 인터페이스를 구현하는 여러 구현체 중 하나를 인스턴스화한다. 이게 팩토리 메서드 디자인 패턴의 역할이다.
fun main() {
val notations = listOf("pa8", "qc3", "nb2", "bd4", "ka1", "ra3")
val pieces = mutableListOf<ChessPiece>()
for (n in notations) {
pieces.add(createPiece(n))
}
println(pieces)
}
interface ChessPiece {
val file: Char
val rank: Char
}
data class Pawn(override val file: Char, override val rank: Char) : ChessPiece
data class Queen(override val file: Char, override val rank: Char) : ChessPiece
data class Bishop(override val file: Char, override val rank: Char) : ChessPiece
data class Knight(override val file: Char, override val rank: Char) : ChessPiece
data class Rook(override val file: Char, override val rank: Char) : ChessPiece
data class King(override val file: Char, override val rank: Char) : ChessPiece
fun createPiece(notation: String): ChessPiece {
val (type, file, rank) = notation.toCharArray()
return when (type) {
'q' -> Queen(file, rank)
'p' -> Pawn(file, rank)
'b' -> Bishop(file, rank)
'k' -> King(file, rank)
'r' -> Rook(file, rank)
'n' -> Knight(file, rank)
else -> throw RuntimeException("알 수 없는 기물 종류: $type")
}
}