Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط 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، يعني كل خيار جديد تعديل الخوارزمية. أما مع الاستراتيجيات، فالخيار الجديد هو فئة أو دالة lambda جديدة، ولا تتغير 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 عندما تكون قيمة mana أكبر من أو تساوي 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اختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: Playground لـ Kotlin