תבנית Strategy
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Kotlin של Coddy. שיעור 45 מתוך 57.
תבנית האסטרטגיה ממקמת כל וריאציה של אלגוריתם במחלקה משלה, מאחורי ממשק משותף. הקוד שמשתמש בה מחזיק אסטרטגיה אחת ויכול להחליף אותה בזמן שהתוכנית פועלת:
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)
}בתוך main:
val checkout = Checkout(Standard())
println(checkout.total(100, 3))
checkout.shipping = Express()
println(checkout.total(100, 3))פלט:
108
121כשלתבנית עיצוב יש מתודה אחת, טיפוס פונקציה מספיק. המחלקה שומרת פונקציה וקוראת לה:
class Formatter(var style: (String) -> String) {
fun show(words: List<String>) = words.joinToString(" ") { style(it) }
}בתוך main:
val f = Formatter { it.uppercase() }
println(f.show(listOf("hello", "world")))
f.style = { it.reversed() }
println(f.show(listOf("hello", "world")))פלט:
HELLO WORLD
olleh dlrowאסטרטגיה מחליפה when בתוך האלגוריתם. עם when, כל אפשרות חדשה מחייבת לערוך את האלגוריתם. עם אסטרטגיות, אפשרות חדשה היא מחלקה או למבדה חדשה, ו-Checkout לעולם לא משתנה:
// ללא אסטרטגיה: כל אפשרות חדשה מחייבת לשנות את הפונקציה הזו
fun cost(kind: String, kg: Int) = when (kind) {
"standard" -> 5 + kg
else -> 15 + kg * 2
}
// עם אסטרטגיה: מוסיפים מחלקה ומשאירים את Checkout ללא שינוי
class Overnight : Shipping {
override fun cost(kg: Int) = 40
}אסטרטגיה נבחרת לעיתים קרובות על סמך נתונים, כמו תוכנית של לקוח, באמצעות מפה או מפעל קטן:
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בתוך main:
println(finalPrice("gold", 200))
println(finalPrice("silver", 200))
println(finalPrice("basic", 200))פלט:
160
180
200אתגר
קלהמתקפה של גיבור היא אסטרטגיה. הממשק Attack (עם name ו-damage(strength)) מסופק. כתבו את Sword (נזק השווה לחוזק כפול 2), את Bow (חוזק ועוד 5) ואת Magic(mana) (חוזק כפול 3 כאשר המאנה היא לפחות 10, אחרת 1). Hero(name, strength, attack) יכול לשנות את attack שלו בכל עת, ו-strike() מחזירה Ada hits with sword for 20 בלי לבדוק באיזו מתקפה הוא משתמש.
הקוד המסופק קורא את name,strength ולאחר מכן פקודות equip bow, equip magic 15, equip sword או strike, ומדפיס את התוצאה של כל מתקפה.
הקוד שלכם ייכתב בקבצים Sword.kt, Bow.kt, Magic.kt, Hero.kt ו-Attack.kt. הקובץ Main.kt מכיל את קוד הקלט/פלט המסופק ואי אפשר לערוך אותו.
נסו בעצמכם
fun main() {
// קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
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()
}
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1מחלקות ואובייקטים
עבודה עם קבציםהגדרת מחלקותבנאים ראשייםבלוקי init ואימות נתוניםבנאים משנייםסיכום – כרטיס ספרייהתרגלו בעצמכם: Kotlin אונליין