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

نمط Strategy

جزء من قسم البرمجة كائنية التوجه في رحلة Swift على Coddy. الدرس 45 من 57.

يضع نمط الاستراتيجية كل متغيّر من خوارزمية في نوع خاص به خلف بروتوكول مشترك. يحتفظ الكود الذي يستخدمه باستراتيجية ويمكنه تبديلها أثناء التشغيل:

protocol ShippingStrategy {
    func cost(weight: Int) -> Int
}
struct Standard: ShippingStrategy { func cost(weight: Int) -> Int { 5 + weight } }
struct Express: ShippingStrategy { func cost(weight: Int) -> Int { 15 + weight * 2 } }

struct Checkout {
    var shipping: any ShippingStrategy
    func total(price: Int, weight: Int) -> Int { price + shipping.cost(weight: weight) }
}

var c = Checkout(shipping: Standard())
print(c.total(price: 100, weight: 3))
c.shipping = Express()
print(c.total(price: 100, weight: 3))

الناتج:

108
121

في Swift، غالبًا ما تكون الإغلاقية كافية لاستراتيجية ذات طريقة واحدة. يخزّن النوع دالةً ويستدعيها:

struct Sorter {
    var rule: (String, String) -> Bool
    func sort(_ words: [String]) -> [String] { words.sorted(by: rule) }
}

let words = ["banana", "kiwi", "apple"]
var s = Sorter(rule: <)
print(s.sort(words))
s.rule = { $0.count < $1.count }
print(s.sort(words))

الناتج:

["apple", "banana", "kiwi"]
["kiwi", "apple", "banana"]

تستبدل الاستراتيجية تعليمة switch داخل الخوارزمية. وتكون المتغيرات الجديدة أنواعًا جديدة (أو إغلاقات)، بينما يظل الكود الذي يستخدمها دون تغيير:

// بدون استراتيجية: كل خيار جديد يعدّل هذه الدالة
func cost(kind: String, weight: Int) -> Int {
    switch kind { case "standard": return 5 + weight; default: return 15 + weight * 2 }
}
// مع استراتيجية: Checkout لا يتغير أبداً
struct Overnight: ShippingStrategy { func cost(weight: Int) -> Int { 40 } }

يمكن اختيار الاستراتيجيات من البيانات، مثلًا من إعدادات المستخدم، باستخدام مصنع صغير:

protocol Discount { func apply(_ price: Int) -> Int }
struct NoDiscount: Discount { func apply(_ p: Int) -> Int { p } }
struct Percent: Discount { let rate: Int; func apply(_ p: Int) -> Int { p - p * rate / 100 } }

func discount(for member: String) -> any Discount {
    member == "gold" ? Percent(rate: 20) : NoDiscount()
}

print(discount(for: "gold").apply(200))
print(discount(for: "basic").apply(200))

الناتج:

160
200
challenge icon

التحدي

سهل

هجوم شخصية اللعبة هو استراتيجية. يحتوي البروتوكول Attack على name وdamage(strength:) -> Int. نفّذ Sword ‏(strength * 2)، وBow ‏(strength + 5)، وMagic ‏(قيمة mana مخزّنة؛ يكون الضرر strength * 3 عندما تكون mana على الأقل 10، و1 otherwise؛ لا تغيّر damage نفسها mana). يخزّن Hero قيمتَي strength وattack، وتُرجع strike() القيمة Ada hits with sword for 20.

يقرأ الكود المزوَّد اسم البطل وقوته، ثم الأوامر equip sword أو equip bow أو equip magic 15 أو strike. ويطبع نتيجة كل ضربة.

ضع الكود الخاص بك في Attack.swift وSword.swift وBow.swift وMagic.swift وHero.swift. يحتوي main.swift على كود الإدخال/الإخراج المزوَّد، ولا يمكن تعديله.

جرّب بنفسك

// كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
var input: [String] = []
while let line = readLine() { input.append(line) }
let head = input[0].split(separator: ",").map(String.init)
var hero = Hero(name: head[0], strength: Int(head[1])!, attack: Sword())
for cmd in input.dropFirst() {
    let p = cmd.split(separator: " ").map(String.init)
    if p[0] == "strike" { print(hero.strike()); continue }
    switch p[1] {
    case "bow": hero.attack = Bow()
    case "magic": hero.attack = Magic(mana: Int(p[2])!)
    default: hero.attack = Sword()
    }
}
quiz iconاختبر نفسك

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

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

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