Patron décorateur
Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 47 sur 57.
Le motif décorateur ajoute un comportement à un objet en l’enveloppant dans un autre objet doté de la même interface. Le décorateur délègue à l’objet qu’il enveloppe et ajoute sa propre partie, ce qui permet d’empiler les décorateurs :
interface Coffee {
fun cost(): Int
fun description(): String
}
class Espresso : Coffee {
override fun cost() = 200
override fun description() = "espresso"
}
class Milk(private val inner: Coffee) : Coffee {
override fun cost() = inner.cost() + 50
override fun description() = inner.description() + ", milk"
}
class Syrup(private val inner: Coffee) : Coffee {
override fun cost() = inner.cost() + 30
override fun description() = inner.description() + ", syrup"
}Dans main :
val order = Syrup(Milk(Milk(Espresso())))
println("${order.description()}: ${order.cost()}")Sortie :
espresso, milk, milk, syrup: 330Lorsque les décorateurs modifient le résultat, leur ordre est important : chacun travaille sur ce que l’objet qui se trouve à l’intérieur de lui a renvoyé :
interface Text {
fun render(): String
}
class Word(private val w: String) : Text {
override fun render() = w
}
class Bracket(private val inner: Text) : Text {
override fun render() = "[" + inner.render() + "]"
}
class Twice(private val inner: Text) : Text {
override fun render() = inner.render() + inner.render()
}Dans main :
println(Twice(Bracket(Word("hi"))).render())
println(Bracket(Twice(Word("hi"))).render())Résultat :
[hi][hi]
[hihi]Avec by, un décorateur n’écrit que les membres qu’il modifie et transmet automatiquement le reste :
interface Source {
fun read(): String
fun name(): String
}
class FileSource(private val content: String) : Source {
override fun read() = content
override fun name() = "notes.txt"
}
class Censored(private val inner: Source, private val word: String) : Source by inner {
override fun read() = inner.read().replace(word, "***")
}Dans main :
val src = Censored(FileSource("the secret plan"), "secret")
println("${src.name()}: ${src.read()}")Sortie :
notes.txt: the *** planLes décorateurs sont combinés pendant l’exécution du programme, par exemple à partir des choix d’un client, tandis qu’une sous-classe fixe une combinaison lors de l’écriture du code. Trois extras sous forme de sous-classes nécessiteraient une classe pour chaque combinaison ; sous forme de décorateurs, fold les empile à partir d’une liste :
interface Coffee {
fun cost(): Int
}
class Espresso : Coffee {
override fun cost() = 200
}
class Extra(private val inner: Coffee, private val price: Int) : Coffee {
override fun cost() = inner.cost() + price
}À l’intérieur de main :
val prices = mapOf("milk" to 50, "syrup" to 30, "cream" to 70)
val choices = listOf("milk", "cream", "milk")
val coffee = choices.fold<String, Coffee>(Espresso()) { c, extra -> Extra(c, prices.getValue(extra)) }
println(coffee.cost())Sortie :
370Défi
FacileLes pizzas sont construites avec des décorateurs de l’interface fournie Pizza (avec price() et toppings()). Écrivez Base (800, sans garnitures) ainsi que les décorateurs Cheese (+150, ajoute cheese), Olives (+100, ajoute olives) et DoubleUp, qui double le prix de tout ce qui se trouve à l’intérieur et répète deux fois ses garnitures. Écrivez ensuite build(names), qui part de Base et l’enveloppe avec un décorateur par nom, dans l’ordre (cheese, olives ou double).
Le code fourni lit des lignes contenant des noms séparés par des virgules, ou plain, construit chaque pizza et affiche 1050: cheese, olives ou 800: plain.
Votre code doit être placé dans Base.kt, Cheese.kt, Olives.kt, DoubleUp.kt, Build.kt et Pizza.kt. Main.kt contient le code d’entrée/sortie fourni 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()
for (line in input) {
val names = if (line == "plain") listOf() else line.split(",")
val pizza = build(names)
val toppings = pizza.toppings()
println("${pizza.price()}: " + if (toppings.isEmpty()) "plain" else toppings.joinToString(", "))
}
}
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