نمط التفويض
جزء من قسم البرمجة كائنية التوجه في رحلة Swift على Coddy. الدرس 46 من 57.
التفويض يتيح لكائنٍ ما إسناد بعض القرارات إلى كائن آخر، هو المفوَّض، من خلال بروتوكول. ينفّذ الكائن مهمته ويسأل المفوَّض عند النقاط التي تختلف. تستخدم أُطر عمل Apple هذا النمط في كل مكان:
protocol DownloadDelegate: AnyObject {
func progress(_ percent: Int)
func finished(_ file: String)
}
final class Downloader {
weak var delegate: DownloadDelegate?
func download(_ file: String) {
for p in [50, 100] { delegate?.progress(p) }
delegate?.finished(file)
}
}
final class Screen: DownloadDelegate {
func progress(_ percent: Int) { print("\(percent)%") }
func finished(_ file: String) { print("done: \(file)") }
}
let screen = Screen()
let d = Downloader()
d.delegate = screen
d.download("photo.png")الناتج:
50%
100%
done: photo.pngخاصية المندوب اختيارية، وتستخدم الاستدعاءات delegate?.، لذلك يعمل الكائن حتى عندما لا يكون هناك من يستمع. وقد تم التصريح عنها باستخدام weak حتى لا يُبقي الكائن على المندوب حيًا بمفرده، مما يتجنب دورات الاحتفاظ بين الكائنات التي تشير إلى بعضها البعض:
weak var delegate: DownloadDelegate? // يتطلب أن يكون البروتوكول للفئات فقط: AnyObject
delegate?.finished(file) // لا يفعل شيئًا عندما لا يوجد delegateيمكن للمفوَّض أيضًا الإجابة عن الأسئلة، مما يتيح له تخصيص سلوك الكائن من دون إنشاء فئة فرعية:
protocol ListDelegate: AnyObject {
func shouldShow(_ item: String) -> Bool
}
final class ListView {
weak var delegate: ListDelegate?
func render(_ items: [String]) -> [String] {
items.filter { delegate?.shouldShow($0) ?? true }
}
}
final class HideDrafts: ListDelegate {
func shouldShow(_ item: String) -> Bool { !item.hasPrefix("draft") }
}
let view = ListView()
let rules = HideDrafts()
print(view.render(["a", "draft b"]))
view.delegate = rules
print(view.render(["a", "draft b"]))الناتج:
["a", "draft b"]
["a"]يختلف التفويض عن نمط المراقب: إذ يمتلك الكائن مفوضًا واحدًا يسأله ويُطلعه، بينما يمكن أن يكون للموضوع العديد من المراقبين الذين يطلعهم فقط.
// التفويض: شريك واحد، قد يجيب على الأسئلة
weak var delegate: TableDelegate?
// المراقب: أي عدد من المستمعين، يُخطَرون فقط
private var observers: [(Event) -> Void] = []التحدي
سهليطلب Checkout من المفوَّض إليه اتخاذ القرارات. يحتوي البروتوكول CheckoutDelegate (المقتصر على الفئات) على discount(for total: Int) -> Int وdidComplete(total: Int). يحتوي Checkout على weak var delegate، وتجمع pay(_ prices: [Int]) -> Int الأسعار، وتطرح خصم المفوَّض إليه (0 من دون مفوَّض إليه)، وتُبلغ المفوَّض إليه بالإجمالي النهائي ثم تُعيده. نفّذ LoyaltyShop، وهو مفوَّض إليه يمنح خصمًا بنسبة 10% على الإجماليات التي تبلغ 100 أو أكثر، ويسجّل كل إجمالي مكتمل في receipts.
يقرأ الكود المزوَّد أسطرًا تحتوي على أسعار مفصولة بفواصل. يُجري عملية الدفع للسطر الأول من دون مفوَّض إليه، وللأسطر الأخرى باستخدام LoyaltyShop، ويطبع كل إجمالي، وأخيرًا receipts: 120 90.
ضع كودك في CheckoutDelegate.swift وCheckout.swift وLoyaltyShop.swift. يحتوي main.swift على كود الإدخال/الإخراج المزوَّد، ولا يمكن تعديله.
جرّب بنفسك
// كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
var input: [String] = []
while let line = readLine() { input.append(line) }
let checkout = Checkout()
let shop = LoyaltyShop()
for (i, line) in input.enumerated() {
if i == 1 { checkout.delegate = shop }
print(checkout.pay(line.split(separator: ",").map { Int($0)! }))
}
print("receipts: " + shop.receipts.map { String($0) }.joined(separator: " "))
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الأصناف والكائنات
العمل مع الملفاتتعريف الأصنافالأساليب وselfالمُهيِّئاتالأصناف هي مراجعمراجعة - بطاقة المكتبة4التغليف
التحكم في الوصولللقراءة فقط من الخارجحماية الحالةالمهيئات القابلة للفشلمراجعة - الحساب البنكي7تعدد الأشكال والأنواع العامة
تعدد الأشكالتحويل الأنواعالدوال العامةالأنواع العامةمراجعة - حاسبة الأشكالتدرّب بنفسك: Playground لـ Swift