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