Wzorzec Singleton
Część sekcji Programowanie obiektowe ścieżki Kotlin w Coddy. Lekcja 42 z 57.
Wzorzec projektowy to sprawdzone rozwiązanie problemu projektowego, który pojawia się wielokrotnie. Wzorzec singleton gwarantuje, że klasa ma dokładnie jedną instancję, do której ma dostęp cały program. W Kotlin deklaracja object jest singletonem: jest tworzona raz, przy pierwszym użyciu, i bezpiecznie, nawet gdy kilka wątków uruchamia się w tym samym czasie:
object Settings {
var theme = "light"
var fontSize = 14
}
fun openEditor() = "editor in ${Settings.theme} theme"Wewnątrz main:
Settings.theme = "dark"
println(openEditor())
println(Settings.fontSize)Wynik:
editor in dark theme
14Singleton przechowuje stan współdzielony przez różne części programu. Zachowaj ten stan jako prywatny i zmieniaj go tylko za pomocą metod, aby każda zmiana odbywała się według tych samych reguł:
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")Wewnątrz main:
home(); about(); home()
println(Metrics.report())Wynik:
home=2, about=1Gdy pojedyncza instancja wymaga danych konfiguracyjnych, klasa z private constructor może utworzyć ją w swoim obiekcie towarzyszącym przy pierwszym żądaniu, a następnie zwracać tę samą instancję:
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 }
}
}Wewnątrz main:
val a = Database.connect("db://main")
val b = Database.connect("db://other")
println(a === b)
println(b.url)Wynik:
true
db://mainKorzystaj z singletonów oszczędnie. Singleton to stan globalny: dowolny kod może go zmienić, co ukrywa, od czego jest zależny, a testy nie mogą zaczynać od świeżej kopii. Singletony sprawdzają się w przypadku elementów, które rzeczywiście występują tylko raz w programie, takich jak konfiguracja lub rejestrator. Gdy obiekt jest potrzebny tylko części programu, przekaż go jako parametr o typie interfejsu, aby test mógł przekazać atrapę zamiast niego:
interface Clock { fun now(): Long }
object SystemClock : Clock { override fun now() = System.currentTimeMillis() }
class Session(private val clock: Clock) { // zależność jest widoczna
val started = clock.now()
}
val session = Session(SystemClock) // test przekazuje atrapę ClockWyzwanie
ŁatwyUzupełnij obiekt singleton Logger. Ma on zmienny minLevel (debug, info, warn lub error, w tej kolejności). log(level, message) zapisuje [WARN] disk low, gdy poziom jest co najmniej równy minLevel, a w przeciwnym razie zlicza wiadomość w dropped, którą kod spoza obiektu może odczytać, ale nie zmienić. entries to tylko do odczytu lista zapisanych wierszy, a count(level) zwraca liczbę zapisanych wierszy o danym poziomie.
Dostarczono funkcje pomocnicze w pliku Functions.kt: korzystają z tego samego obiektu Logger z innego pliku. Dostarczony kod odczytuje minimalny poziom, a następnie wiersze level,message, po czym wypisuje każdy wpis oraz errors: 1, dropped: 2.
Twój kod umieść w plikach Logger.kt i Functions.kt. Main.kt zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
fun main() {
// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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}")
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasKonstruktory główneBloki init i walidacjaKonstruktory dodatkowePodsumowanie — karta biblioteczna4Dziedziczenie
Klasy otwarte i podklasyNadpisywanie składowychsuper i konstruktoryKlasy abstrakcyjnePodsumowanie – katalog pracowników7Klasy specjalne
Klasy danychKlasy wyliczenioweHierarchie zapieczętowaneequals, hashCode, toStringPodsumowanie — śledzenie zamówień10Wzorce projektowe, część 2
Delegowanie za pomocą byWzorzec dekoratoraWzorzec budowniczegoWzorzec stanu2Właściwości i hermetyzacja
Własne akcesoryModyfikatory widocznościPrywatne setterylazy i lateinitPowtórka – konto bankowe5Interfejsy
Deklarowanie interfejsówMetody domyślneWiele interfejsówInterfejsy jako typyPowtórzenie – metody płatności8Typy generyczne
Funkcje generyczneKlasy generyczneOgraniczenia typówKowariancja i kontrawariancja: in i outPodsumowanie — kolejka priorytetowa11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książekPoćwicz samodzielnie: Edytor online Kotlin