Patron Factory
Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 43 sur 57.
Le modèle factory centralise la décision concernant la classe à créer. L’appelant décrit ce dont il a besoin et reçoit le type de l’interface, sans nommer de classe concrète :
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
}
}Dans main :
println(NotifierFactory.create("sms")?.send("ada"))
println(NotifierFactory.create("fax")?.send("bo"))Sortie :
sms to ada
nullEn Kotlin, une fabrique est souvent une fonction dans l’objet compagnon de l’interface ou de la classe de base, de sorte que les appelants écrivent Shape.from("circle 2"). Elle peut analyser et vérifier l’entrée avant de créer quoi que ce soit :
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
}Dans main :
println(Shape.from("square 3")?.area())
println(Shape.from("circle x")?.area())Résultat :
9.0
nullLes appelants ne voient que l’interface, donc la fabrique peut modifier la classe qu’elle renvoie sans les toucher. Ici, les petites données restent en mémoire et les grandes données sont écrites sur le disque :
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")À l’intérieur de main :
println(storageFor(5).describe())
println(storageFor(500).describe())Sortie :
memory
disk at /tmp/cacheUne fabrique peut également être une valeur : une fonction de type (String) -> Animal crée des objets sans que l’appelant connaisse leur classe. Une map associant des noms à de telles fonctions est un registre, et une référence de constructeur telle que ::Dog est une fonction fabrique prête à l’emploi :
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"
}Dans 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()}")Sortie :
Tom says MeowDéfi
FacilePlusieurs exportateurs implémentent l’interface fournie Exporter (extension et export(title, lines)). Écrivez MarkdownExporter (extension md : une ligne # Title et une ligne - item par ligne), CsvExporter (extension csv : title,Title, puis line,item pour chaque ligne) et PlainExporter (extension txt : le titre en lettres majuscules, puis les lignes inchangées). Écrivez ensuite l’objet fabrique ExporterFactory : create(format) renvoie l’exportateur correspondant à markdown, csv ou plain, sans tenir compte de la casse, ou null, et supported() renvoie les noms des formats.
Le code fourni lit un format, un titre et les lignes, puis affiche report.md et le texte exporté, ou unsupported format pdf; try markdown, csv, plain.
Votre code doit se trouver dans MarkdownExporter.kt, CsvExporter.kt, PlainExporter.kt, ExporterFactory.kt et Exporter.kt. Main.kt contient le code fourni pour les entrées et sorties et ne peut pas être modifié.
Essayez vous-même
fun main() {
// Code d'entrée/sortie fourni : le garder tel quel
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)))
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Classes et objets
Travailler avec des fichiersDéfinir des classesConstructeurs primairesBlocs init et validationConstructeurs secondairesRécapitulatif - Carte de bibliothèque4Héritage
Classes ouvertes et sous-classesRedéfinition des membressuper et constructeursClasses abstraitesRécapitulatif – Annuaire du personnel2Propriétés et encapsulation
Accesseurs personnalisésModificateurs de visibilitéSetters privéslazy et lateinitRécapitulatif - Compte bancaire5Interfaces
Déclarer des interfacesMéthodes par défautInterfaces multiplesInterfaces en tant que typesRécapitulatif - Méthodes de paiement8Génériques
Fonctions génériquesClasses génériquesContraintes de typeVariance avec in et outRécapitulatif - File de priorité11Projet : Gestion de bibliothèque
Livres et membresEmprunter des livresEntraînez-vous par vous-même : Playground Kotlin