Padrão Factory
Parte da seção Programação Orientada a Objetos do Journey de Kotlin da Coddy. Lição 43 de 57.
O padrão de fábrica coloca a decisão sobre qual classe criar em um único lugar. O chamador descreve o que precisa e recebe o tipo da interface, sem nomear uma 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
}
}Dentro de main:
println(NotifierFactory.create("sms")?.send("ada"))
println(NotifierFactory.create("fax")?.send("bo"))Saída:
sms to ada
nullEm Kotlin, uma fábrica costuma ser uma função no objeto companion da interface ou da classe base, então quem chama escreve Shape.from("circle 2"). Ela pode analisar e verificar a entrada antes de criar qualquer coisa:
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())Saída:
9.0
nullOs chamadores veem apenas a interface, então a fábrica pode alterar qual classe retorna sem precisar modificá-los. Aqui, os dados pequenos permanecem na memória e os dados grandes vão para o 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())Saída:
memory
disk at /tmp/cacheUma fábrica também pode ser um valor: uma função do tipo (String) -> Animal cria objetos sem que o chamador saiba qual é a classe deles. Um mapa de nomes para essas funções é um registro, e uma referência de construtor como ::Dog é uma função de fábrica 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"
}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()}")Saída:
Tom says MeowDesafio
FácilVários exportadores implementam a interface fornecida Exporter (extension e export(title, lines)). Escreva MarkdownExporter (extensão md: uma linha # Title e uma linha - item para cada linha), CsvExporter (extensão csv: title,Title e, depois, line,item para cada linha) e PlainExporter (extensão txt: o título em letras maiúsculas e, depois, as linhas inalteradas). Em seguida, escreva o objeto fábrica ExporterFactory: create(format) retorna o exportador para markdown, csv ou plain, ignorando maiúsculas e minúsculas, ou null, e supported() retorna os nomes dos formatos.
O código fornecido lê um formato, um título e as linhas, e imprime report.md e o texto exportado, ou unsupported format pdf; try markdown, csv, plain.
Seu código deve ficar em MarkdownExporter.kt, CsvExporter.kt, PlainExporter.kt, ExporterFactory.kt e Exporter.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()
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 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