싱글턴 패턴
Coddy Kotlin 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 42번째.
디자인 패턴은 반복해서 발생하는 디자인 문제에 대한 검증된 해결책입니다. 싱글턴 패턴은 클래스에 전체 프로그램이 접근할 수 있는 인스턴스가 정확히 하나만 있도록 보장합니다. Kotlin에서 object 선언은 싱글턴입니다. 처음 사용될 때 한 번 생성되며, 여러 스레드가 동시에 시작될 때도 안전합니다:
object Settings {
var theme = "light"
var fontSize = 14
}
fun openEditor() = "editor in ${Settings.theme} theme"main 내부에서:
Settings.theme = "dark"
println(openEditor())
println(Settings.fontSize)출력:
editor in dark theme
14싱글턴은 프로그램의 여러 부분이 공유하는 상태를 보유합니다. 해당 상태를 비공개로 유지하고 메서드를 통해서만 변경하여 모든 변경이 동일한 규칙을 따르도록 하세요:
object Metrics {
private val hits = mutableMapOf<String, Int>()
fun hit(page: String) { hits[page] = (hits[page] ?: 0) + 1 }
fun report() = hits.entries.joinToString { "${it.key}=${it.value}" }
}
fun home() = Metrics.hit("home")
fun about() = Metrics.hit("about")main 내부에서:
home(); about(); home()
println(Metrics.report())출력:
home=2, about=1단일 인스턴스에 설정 데이터가 필요할 때는 private constructor가 있는 클래스가 첫 번째 요청 시 companion object에서 인스턴스를 생성하고 이후에는 동일한 인스턴스를 반환할 수 있습니다:
class Database private constructor(val url: String) {
companion object {
private var instance: Database? = null
fun connect(url: String): Database =
instance ?: Database(url).also { instance = it }
}
}main 내부:
val a = Database.connect("db://main")
val b = Database.connect("db://other")
println(a === b)
println(b.url)출력:
true
db://main싱글턴은 신중하게 사용하세요. 싱글턴은 전역 상태이므로 어떤 코드든 이를 변경할 수 있고, 누가 이에 의존하는지 알기 어렵게 만들며, 테스트를 새로운 복사본에서 시작할 수 없게 합니다. 구성이나 로거처럼 정말로 프로그램마다 하나만 존재해야 하는 경우에 적합합니다. 프로그램의 일부만 객체를 필요로 한다면 인터페이스 타입의 매개변수로 전달하세요. 그러면 테스트에서 대신 가짜 객체를 전달할 수 있습니다:
interface Clock { fun now(): Long }
object SystemClock : Clock { override fun now() = System.currentTimeMillis() }
class Session(private val clock: Clock) { // 의존성이 보입니다
val started = clock.now()
}
val session = Session(SystemClock) // 테스트가 가짜 Clock을 전달합니다챌린지
쉬움싱글턴 Logger를 완성하세요. 변경 가능한 minLevel(순서대로 debug, info, warn 또는 error)이 있습니다. log(level, message)는 레벨이 minLevel 이상이면 [WARN] disk low를 저장하고, 그렇지 않으면 메시지를 dropped에 집계합니다. dropped는 코드 외부에서 읽을 수 있지만 변경할 수는 없습니다. entries는 저장된 줄의 읽기 전용 목록이며, count(level)은 해당 레벨을 가진 저장된 줄의 개수를 반환합니다.
Functions.kt의 도우미 함수는 제공됩니다. 이 함수들은 다른 파일에서 동일한 Logger에 접근합니다. 제공된 코드는 최소 레벨과 level,message 형식의 줄을 읽은 다음 모든 항목과 errors: 1, dropped: 2를 출력합니다.
코드는 Logger.kt와 Functions.kt에 작성하세요. Main.kt에는 제공된 입력/출력 코드가 들어 있으며 편집할 수 없습니다.
직접 해보기
fun main() {
// 제공된 입출력 코드: 있는 그대로 유지하세요
val input = generateSequence(::readLine).toList()
Logger.minLevel = input[0]
for (line in input.drop(1)) {
val (level, message) = line.split(",", limit = 2)
when (level) {
"debug" -> debug(message)
"info" -> info(message)
"warn" -> warn(message)
else -> error(message)
}
}
for (e in Logger.entries) println(e)
println("errors: ${Logger.count("error")}, dropped: ${Logger.dropped}")
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: Kotlin 플레이그라운드