Patrón Factory
Parte de la sección Programación orientada a objetos del Journey de Kotlin de Coddy. Lección 43 de 57.
El patrón factory concentra en un solo lugar la decisión sobre qué clase crear. La persona que llama describe lo que necesita y recibe el tipo de interfaz, sin nombrar una clase 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
}
}Dentro de main:
println(NotifierFactory.create("sms")?.send("ada"))
println(NotifierFactory.create("fax")?.send("bo"))Salida:
sms to ada
nullEn Kotlin, una fábrica suele ser una función en el objeto complementario de la interfaz o de la clase base, por lo que quienes llaman escriben Shape.from("circle 2"). Puede analizar y comprobar la entrada antes de crear nada:
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 de main:
println(Shape.from("square 3")?.area())
println(Shape.from("circle x")?.area())Salida:
9.0
nullLos llamadores solo ven la interfaz, por lo que la fábrica puede cambiar la clase que devuelve sin modificarlos. Aquí, los datos pequeños permanecen en la memoria y los datos grandes se guardan en el 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")Dentro de main:
println(storageFor(5).describe())
println(storageFor(500).describe())Salida:
memory
disk at /tmp/cacheUna fábrica también puede ser un valor: una función del tipo (String) -> Animal crea objetos sin que quien la llama conozca su clase. Un mapa de nombres a dichas funciones es un registro, y una referencia a un constructor como ::Dog es una función de fábrica lista para usar:
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"
}Dentro de 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()}")Salida:
Tom says MeowDesafío
FácilVarios exportadores implementan la interfaz proporcionada Exporter (extension y export(title, lines)). Escribe MarkdownExporter (extensión md: una línea # Title y una línea - item por cada línea), CsvExporter (extensión csv: title,Title y después line,item por cada línea) y PlainExporter (extensión txt: el título en letras mayúsculas y después las líneas sin cambios). Después escribe el objeto fábrica ExporterFactory: create(format) devuelve el exportador para markdown, csv o plain, ignorando las mayúsculas y minúsculas, o null, y supported() devuelve los nombres de los formatos.
El código proporcionado lee un formato, un título y las líneas, e imprime report.md y el texto exportado, o unsupported format pdf; try markdown, csv, plain.
Tu código va en MarkdownExporter.kt, CsvExporter.kt, PlainExporter.kt, ExporterFactory.kt y Exporter.kt. Main.kt contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
fun main() {
// Código de entrada/salida suministrado: déjalo tal como está
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)))
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Clases y objetos
Trabajar con archivosDefinir clasesConstructores primariosBloques init y validaciónConstructores secundariosRepaso - Tarjeta de biblioteca4Herencia
Clases abiertas y subclasesSobrescritura de miembrossuper y constructoresClases abstractasRepaso: directorio del personal2Propiedades y encapsulación
Accesores personalizadosModificadores de visibilidadSetters privadoslazy y lateinitRepaso: cuenta bancaria5Interfaces
Declaración de interfacesMétodos predeterminadosInterfaces múltiplesInterfaces como tiposRepaso: métodos de pago8Genéricos
Funciones genéricasClases genéricasRestricciones de tiposVariancia con in y outRepaso - Cola de prioridad11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de librosPractica por tu cuenta: Playground de Kotlin