Menu
Coddy logo textTech

Strategiemuster

Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 45 von 57.

Das Strategy-Muster platziert jede Variante eines Algorithmus in einer eigenen Klasse hinter einer gemeinsamen Schnittstelle. Der Code, der es verwendet, enthält eine Strategie und kann sie während der Programmausführung ersetzen:

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)
}

Innerhalb von main:

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

Ausgabe:

108
121

Wenn eine Strategie nur eine Methode hat, reicht ein Funktionstyp aus. Die Klasse speichert eine Funktion und ruft sie auf:

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

In main:

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

Ausgabe:

HELLO WORLD
olleh dlrow

Eine Strategie ersetzt ein when innerhalb des Algorithmus. Beim when bedeutet jede neue Option, dass der Algorithmus bearbeitet werden muss. Bei Strategien ist eine neue Option eine neue Klasse oder Lambda-Funktion, und Checkout ändert sich nie:

// ohne Strategie: jede neue Option ändert diese Funktion
fun cost(kind: String, kg: Int) = when (kind) {
    "standard" -> 5 + kg
    else -> 15 + kg * 2
}
// mit Strategie: eine Klasse hinzufügen, Checkout unberührt lassen
class Overnight : Shipping {
    override fun cost(kg: Int) = 40
}

Eine Strategie wird oft anhand von Daten ausgewählt, beispielsweise anhand des Tarifs eines Kunden, mit einer Map oder einer kleinen Factory:

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

Innerhalb von main:

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

Ausgabe:

160
180
200
challenge icon

Aufgabe

Einfach

Der Angriff eines Helden ist eine Strategie. Die Schnittstelle Attack (mit name und damage(strength)) wird bereitgestellt. Schreibe Sword (Schaden gleich Stärke mal 2), Bow (Stärke plus 5) und Magic(mana) (Stärke mal 3, wenn das Mana mindestens 10 beträgt, andernfalls 1). Hero(name, strength, attack) kann seinen attack jederzeit ändern, und strike() gibt Ada hits with sword for 20 zurück, ohne zu prüfen, welchen Angriff er besitzt.

Der bereitgestellte Code liest name,strength und anschließend die Befehle equip bow, equip magic 15, equip sword oder strike ein und gibt das Ergebnis jedes Angriffs aus.

Dein Code gehört in Sword.kt, Bow.kt, Magic.kt, Hero.kt und Attack.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.

Probier es selbst

fun main() {
    // Gelieferter Ein-/Ausgabe-Code: belasse ihn wie er ist
    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 iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Kotlin-Playground