코틀린 디자인 패턴

생성 패턴

언제 그리고 어떻게 객체를 생성할지에 관한 디자인 패턴.

  • 싱글톤 패턴
  • 팩토리 메서드 패턴
  • 추상 팩토리 패턴
  • 빌더 패턴
  • 프로토타입 패턴

싱글톤 패턴

  • 시스템에 인스턴스가 딱 하나만 존재
    • 프로그램이 시작되자마자 싱글톤 인스턴스가 만들어지면 안됨
    • 인스턴스 생성은 필요한 첫 순간에 이뤄져야 함
    • 두 스레드가 동시에 싱글톤 객체를 생성하려고 할 때 두 스레드가 같은 인스턴스를 획득해야 함
  • 시스템의 모든 부분에서 인스턴스에 접근할 수 있어야 함

=> 코틀린에선 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")
    }
}

정적 팩토리 메서드

results matching ""

    No results matching ""