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
121Wenn 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 dlrowEine 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) ?: priceInnerhalb von main:
println(finalPrice("gold", 200))
println(finalPrice("silver", 200))
println(finalPrice("basic", 200))Ausgabe:
160
180
200Aufgabe
EinfachDer 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()
}
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Klassen und Objekte
Arbeiten mit DateienKlassen definierenPrimärkonstruktoreninit-Blöcke und ValidierungSekundärkonstruktorenRückblick – Bibliotheksausweis4Vererbung
Offene Klassen und UnterklassenÜberschreiben von Membernsuper und KonstruktorenAbstrakte KlassenRückblick – Mitarbeiterverzeichnis2Eigenschaften und Kapselung
Benutzerdefinierte ZugriffsmethodenSichtbarkeitsmodifikatorenPrivate Setterlazy und lateinitWiederholung – Bankkonto5Schnittstellen
Schnittstellen deklarierenStandardmethodenMehrere SchnittstellenSchnittstellen als TypenRückblick – Zahlungsmethoden8Generics
Generische FunktionenGenerische KlassenTypbeschränkungenVarianz mit in und outRückblick – Prioritätswarteschlange11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Kotlin-Playground