Pattern Factory
Fa parte della sezione Programmazione orientata agli oggetti del percorso Kotlin di Coddy. Lezione 43 di 57.
Il pattern factory concentra in un unico punto la decisione su quale classe creare. Il chiamante descrive ciò di cui ha bisogno e riceve il tipo di interfaccia, senza indicare una classe concreta:
interface Notifier {
fun send(to: String): String
}
class EmailNotifier : Notifier {
override fun send(to: String) = "email to $to"
}
class SmsNotifier : Notifier {
override fun send(to: String) = "sms to $to"
}
object NotifierFactory {
fun create(channel: String): Notifier? = when (channel) {
"email" -> EmailNotifier()
"sms" -> SmsNotifier()
else -> null
}
}All'interno di main:
println(NotifierFactory.create("sms")?.send("ada"))
println(NotifierFactory.create("fax")?.send("bo"))Output:
sms to ada
nullIn Kotlin una factory è spesso una funzione nell'oggetto companion dell'interfaccia o della classe base, quindi chi chiama scrive Shape.from("circle 2"). Può analizzare e controllare l'input prima di creare qualsiasi cosa:
interface Shape {
fun area(): Double
companion object {
fun from(text: String): Shape? {
val p = text.split(" ")
val n = p.getOrNull(1)?.toDoubleOrNull() ?: return null
return when (p[0]) {
"circle" -> Circle(n)
"square" -> Square(n)
else -> null
}
}
}
}
class Circle(val r: Double) : Shape {
override fun area() = 3.14 * r * r
}
class Square(val s: Double) : Shape {
override fun area() = s * s
}Dentro main:
println(Shape.from("square 3")?.area())
println(Shape.from("circle x")?.area())Output:
9.0
nullI chiamanti vedono solo l’interfaccia, quindi la factory può cambiare la classe che restituisce senza modificarli. Qui i dati piccoli restano in memoria e quelli grandi vengono salvati su disco:
interface Storage {
fun describe(): String
}
class MemoryStorage : Storage {
override fun describe() = "memory"
}
class DiskStorage(val path: String) : Storage {
override fun describe() = "disk at $path"
}
fun storageFor(sizeMb: Int): Storage = if (sizeMb < 100) MemoryStorage() else DiskStorage("/tmp/cache")All’interno di main:
println(storageFor(5).describe())
println(storageFor(500).describe())Output:
memory
disk at /tmp/cacheUna factory può anche essere un valore: una funzione di tipo (String) -> Animal crea oggetti senza che chi la chiama conosca la loro classe. Una mappa dai nomi a tali funzioni è un registro, e un riferimento a un costruttore come ::Dog è una funzione factory già pronta:
open class Animal(val name: String) {
open fun sound() = "..."
}
class Dog(name: String) : Animal(name) {
override fun sound() = "Woof"
}
class Cat(name: String) : Animal(name) {
override fun sound() = "Meow"
}All'interno di main:
val makers: Map<String, (String) -> Animal> = mapOf("dog" to ::Dog, "cat" to ::Cat)
val pet = makers.getValue("cat")("Tom")
println("${pet.name} says ${pet.sound()}")Output:
Tom says MeowSfida
FacileDiversi esportatori implementano l’interfaccia fornita Exporter (extension e export(title, lines)). Scrivi MarkdownExporter (estensione md: una riga # Title e una riga - item per ogni riga), CsvExporter (estensione csv: title,Title e poi line,item per ogni riga) e PlainExporter (estensione txt: il titolo in lettere maiuscole, poi le righe invariate). Scrivi quindi l’oggetto factory ExporterFactory: create(format) restituisce l’esportatore per markdown, csv o plain, ignorando le maiuscole e le minuscole, oppure null, mentre supported() restituisce i nomi dei formati.
Il codice fornito legge un formato, un titolo e le righe, e stampa report.md e il testo esportato, oppure unsupported format pdf; try markdown, csv, plain.
Il tuo codice va inserito in MarkdownExporter.kt, CsvExporter.kt, PlainExporter.kt, ExporterFactory.kt e Exporter.kt. Main.kt contiene il codice di input/output fornito e non può essere modificato.
Provalo tu
fun main() {
// Supplied input/output code: keep it as it is
val input = generateSequence(::readLine).toList()
val exporter = ExporterFactory.create(input[0])
if (exporter == null) {
println("unsupported format ${input[0]}; try ${ExporterFactory.supported().joinToString(", ")}")
} else {
println("report.${exporter.extension}")
println(exporter.export(input[1], input.drop(2)))
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Classi e oggetti
Lavorare con i fileDefinire le classiCostruttori primariBlocchi init e convalidaCostruttori secondariRiepilogo - Tessera della biblioteca4Ereditarietà
Classi aperte e sottoclassiRidefinizione dei membrisuper e costruttoriClassi astratteRiepilogo - Elenco del personale2Proprietà e incapsulamento
Accessor personalizzatiModificatori di visibilitàSetter privatilazy e lateinitRipasso - Conto bancario5Interfacce
Dichiarare le interfacceMetodi predefinitiInterfacce multipleLe interfacce come tipiRiepilogo - Metodi di pagamento8Tipi generici
Funzioni genericheClassi genericheVincoli sui tipiVarianza con in e outRiepilogo - Coda con priorità11Progetto: Gestione della biblioteca
Libri e sociPrendere in prestito i libriEsercitati da solo: Playground Kotlin