Menu
Coddy logo textTech

Singleton Kalıbı

Coddy'nin Kotlin Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 42 / 57.

Tasarım deseni, tekrar tekrar ortaya çıkan bir tasarım problemine yönelik kanıtlanmış bir çözümdür. Singleton deseni, bir sınıfın tüm programın erişebileceği tam olarak bir örneğe sahip olmasını sağlar. Kotlin'de bir object bildirimi bir singleton'dır: ilk kullanımda bir kez oluşturulur ve birkaç iş parçacığı aynı anda başlasa bile güvenli bir şekilde oluşturulur:

object Settings {
    var theme = "light"
    var fontSize = 14
}

fun openEditor() = "editor in ${Settings.theme} theme"

main içinde:

Settings.theme = "dark"
println(openEditor())
println(Settings.fontSize)

Çıktı:

editor in dark theme
14

Singleton, programun farklı bölümlerinin paylaştığı durumu tutar. Bu durumu gizli tutun ve yalnızca metotlar aracılığıyla değiştirin; böylece her değişiklik aynı kurallardan geçer:

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 içinde:

home(); about(); home()
println(Metrics.report())

Çıktı:

home=2, about=1

Tek örneğin kurulum verilerine ihtiyaç duyduğu durumda, private constructor içeren bir sınıf, ilk istekte bunu companion object içinde oluşturabilir ve sonrasında aynı örneği döndürebilir:

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 içinde:

val a = Database.connect("db://main")
val b = Database.connect("db://other")
println(a === b)
println(b.url)

Çıktı:

true
db://main

Singleton'ları ölçülü kullanın. Bir singleton global durumdur: herhangi bir kod onu değiştirebilir; bu, ona kimin bağlı olduğunu gizler ve testler yeni bir kopyayla başlayamaz. Gerçekten program başına bir tane olması gereken şeyler için, örneğin yapılandırma veya günlükleyici için uygundurlar. Programın yalnızca bir bölümü bir nesneye ihtiyaç duyduğunda, onu bir arayüz olarak türlendirip parametre şeklinde aktarın; böylece bir test bunun yerine sahte bir nesne geçirebilir:

interface Clock { fun now(): Long }
object SystemClock : Clock { override fun now() = System.currentTimeMillis() }

class Session(private val clock: Clock) {      // bağımlılık görünür
    val started = clock.now()
}
val session = Session(SystemClock)             // bir test sahte bir Clock geçirir
challenge icon

Görev

Kolay

Logger singleton'ını tamamla. Değiştirilebilir bir minLevel'a sahiptir (debug, info, warn veya error; bu sırayla). Seviye en az minLevel olduğunda log(level, message), [WARN] disk low değerini saklar; aksi durumda mesajı dropped içinde sayar. Bu değer kod dışından okunabilir ancak değiştirilemez. entries, saklanan satırların salt okunur listesidir ve count(level), o seviyeye sahip kaç saklanan satır olduğunu döndürür.

Functions.kt içindeki yardımcı işlevler sağlanmıştır: başka bir dosyadan aynı Logger'a erişirler. Sağlanan kod minimum seviyeyi ve ardından level,message satırlarını okur, sonra her girdiyi ve errors: 1, dropped: 2 değerini yazdırır.

Kodun Logger.kt ve Functions.kt dosyalarına yazılmalıdır. Main.kt, sağlanan girdi/çıktı kodunu içerir ve düzenlenemez.

Kendin dene

fun main() {
    // Sağlanan girdi/çıktı kodu: olduğu gibi bırakın
    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}")
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler

Kendi başına pratik yap: Kotlin playground