Menu
Coddy logo textTech

תבנית 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
challenge icon

אתגר

קל

המתקפה של גיבור היא אסטרטגיה. הממשק 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()
        }
    }
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: Kotlin אונליין