نمط 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التحدي
سهلهجوم البطل هو استراتيجية. الواجهة 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()
}
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الفئات والكائنات
العمل مع الملفاتتعريف الفئاتالمنشئات الأساسيةكتل init والتحقق من الصحةالمنشئات الثانويةمراجعة - بطاقة المكتبة4الوراثة
الأصناف المفتوحة والأصناف الفرعيةتجاوز الأعضاءsuper والمنشئاتالأصناف المجرّدةمراجعة - دليل الموظفينتدرّب بنفسك: Playground لـ Kotlin