Menu
Coddy logo textTech

Patron Strategy

Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 45 sur 57.

Le modèle de stratégie place chaque variante d’un algorithme dans sa propre classe, derrière une interface commune. Le code qui l’utilise conserve une stratégie et peut la remplacer pendant l’exécution du programme :

interface Shipping {
    fun cost(kg: Int): Int
}
class Standard : Shipping {
    override fun cost(kg: Int) = 5 + kg
}
class Express : Shipping {
    override fun cost(kg: Int) = 15 + kg * 2
}

class Checkout(var shipping: Shipping) {
    fun total(price: Int, kg: Int) = price + shipping.cost(kg)
}

Dans main :

val checkout = Checkout(Standard())
println(checkout.total(100, 3))
checkout.shipping = Express()
println(checkout.total(100, 3))

Sortie :

108
121

Lorsqu’une stratégie ne comporte qu’une seule méthode, un type fonction suffit. La classe stocke une fonction et l’appelle :

class Formatter(var style: (String) -> String) {
    fun show(words: List<String>) = words.joinToString(" ") { style(it) }
}

Dans main :

val f = Formatter { it.uppercase() }
println(f.show(listOf("hello", "world")))
f.style = { it.reversed() }
println(f.show(listOf("hello", "world")))

Sortie :

HELLO WORLD
olleh dlrow

Une stratégie remplace un when à l’intérieur de l’algorithme. Avec le when, chaque nouvelle option implique de modifier l’algorithme. Avec les stratégies, une nouvelle option correspond à une nouvelle classe ou lambda, et Checkout ne change jamais :

// sans stratégie : chaque nouvelle option modifie cette fonction
fun cost(kind: String, kg: Int) = when (kind) {
    "standard" -> 5 + kg
    else -> 15 + kg * 2
}
// avec une stratégie : ajoutez une classe, laissez Checkout tranquille
class Overnight : Shipping {
    override fun cost(kg: Int) = 40
}

Une stratégie est souvent choisie à partir de données, telles que le forfait d’un client, avec une map ou une petite fabrique :

val discounts: Map<String, (Int) -> Int> = mapOf(
    "gold" to { price -> price * 80 / 100 },
    "silver" to { price -> price * 90 / 100 }
)

fun finalPrice(plan: String, price: Int) = discounts[plan]?.invoke(price) ?: price

À l’intérieur de main :

println(finalPrice("gold", 200))
println(finalPrice("silver", 200))
println(finalPrice("basic", 200))

Sortie :

160
180
200
challenge icon

Défi

Facile

L’attaque d’un héros est une stratégie. L’interface Attack (avec name et damage(strength)) est fournie. Écrivez Sword (dégâts égaux à la force multipliée par 2), Bow (force plus 5) et Magic(mana) (force multipliée par 3 lorsque le mana est supérieur ou égal à 10, sinon 1). Hero(name, strength, attack) peut modifier son attack à tout moment, et strike() renvoie Ada hits with sword for 20 sans vérifier quelle attaque il possède.

Le code fourni lit name,strength, puis les commandes equip bow, equip magic 15, equip sword ou strike, et affiche le résultat de chaque attaque.

Votre code doit être placé dans Sword.kt, Bow.kt, Magic.kt, Hero.kt et Attack.kt. Main.kt contient le code fourni pour les entrées et les 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 (name, strength) = input[0].split(",")
    val hero = Hero(name, strength.toInt(), Sword())
    for (cmd in input.drop(1)) {
        val p = cmd.split(" ")
        if (p[0] == "strike") {
            println(hero.strike())
            continue
        }
        hero.attack = when (p[1]) {
            "bow" -> Bow()
            "magic" -> Magic(p[2].toInt())
            else -> Sword()
        }
    }
}
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Playground Kotlin