Padrão Singleton
Parte da seção Programação Orientada a Objetos do Journey de Kotlin da Coddy. Lição 42 de 57.
Um padrão de projeto é uma solução comprovada para um problema de projeto que surge repetidamente. O padrão singleton garante que uma classe tenha exatamente uma instância que todo o programa possa acessar. Em Kotlin, uma declaração object é um singleton: ela é criada uma vez, no primeiro uso, e com segurança mesmo quando várias threads são iniciadas ao mesmo tempo:
object Settings {
var theme = "light"
var fontSize = 14
}
fun openEditor() = "editor in ${Settings.theme} theme"Dentro de main:
Settings.theme = "dark"
println(openEditor())
println(Settings.fontSize)Saída:
editor in dark theme
14O singleton mantém um estado que diferentes partes do programa compartilham. Mantenha esse estado privado e altere-o somente por meio de métodos, para que toda mudança siga as mesmas regras:
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")Dentro de main:
home(); about(); home()
println(Metrics.report())Saída:
home=2, about=1Quando a instância única precisa de dados de configuração, uma classe com um private constructor pode criá-la em seu objeto companion na primeira solicitação e retornar a mesma instância depois:
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 }
}
}Dentro de main:
val a = Database.connect("db://main")
val b = Database.connect("db://other")
println(a === b)
println(b.url)Saída:
true
db://mainUse singletons com moderação. Um singleton é um estado global: qualquer código pode alterá-lo, o que oculta quem depende dele, e os testes não podem começar a partir de uma cópia nova. Eles são adequados para o que realmente existe uma vez por programa, como a configuração ou um logger. Quando apenas uma parte do programa precisa de um objeto, passe-o como parâmetro, tipado como uma interface, para que um teste possa passar um objeto falso em seu lugar:
interface Clock { fun now(): Long }
object SystemClock : Clock { override fun now() = System.currentTimeMillis() }
class Session(private val clock: Clock) { // a dependência é visível
val started = clock.now()
}
val session = Session(SystemClock) // um teste passa um Clock falsoDesafio
FácilComplete o singleton Logger. Ele tem um minLevel alterável (debug, info, warn ou error, nessa ordem). log(level, message) armazena [WARN] disk low quando o nível é pelo menos minLevel e, caso contrário, contabiliza a mensagem em dropped, que pode ser lido fora do código, mas não alterado. entries é uma lista somente para leitura das linhas armazenadas, e count(level) retorna quantas linhas armazenadas têm esse nível.
As funções auxiliares em Functions.kt são fornecidas: elas acessam o mesmo Logger a partir de outro arquivo. O código fornecido lê o nível mínimo e, em seguida, as linhas level,message, e imprime cada entrada e errors: 1, dropped: 2.
Seu código deve estar em Logger.kt e Functions.kt. Main.kt contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
fun main() {
// Código de entrada/saída fornecido: mantenha-o como está
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}")
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesConstrutores PrimáriosBlocos init e ValidaçãoConstrutores SecundáriosRecapitulação - Cartão da Biblioteca4Herança
Classes Abertas e SubclassesSobrescrevendo Membrossuper e ConstrutoresClasses AbstratasRevisão - Diretório de Funcionários2Propriedades e Encapsulamento
Acessores PersonalizadosModificadores de VisibilidadeSetters Privadoslazy e lateinitRevisão - Conta Bancária5Interfaces
Declarando InterfacesMétodos PadrãoMúltiplas InterfacesInterfaces como TiposRecapitulação - Métodos de Pagamento8Genéricos
Funções genéricasClasses genéricasRestrições de tipoVariância com in e outRecapitulação - Fila de prioridade11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Playground de Kotlin