نمط المُزيّن
جزء من قسم البرمجة كائنية التوجه في رحلة Swift على Coddy. الدرس 47 من 57.
يضيف نمط decorator سلوكًا إلى كائن من خلال تغليفه بكائن آخر يستخدم البروتوكول نفسه. يمرّر الغلاف الاستدعاء إلى الكائن المغلَّف ويضيف جزأه الخاص قبل ذلك أو بعده:
protocol Coffee {
func cost() -> Int
func label() -> String
}
struct Espresso: Coffee {
func cost() -> Int { 200 }
func label() -> String { "espresso" }
}
struct WithMilk: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 50 }
func label() -> String { base.label() + " + milk" }
}
let drink = WithMilk(base: Espresso())
print(drink.label())
print(drink.cost())الناتج:
espresso + milk
250بما أن المُزخرف هو نفسه Coffee، يمكن للمُزخرفات تغليف بعضها بأي تركيبة وترتيب، من دون الحاجة إلى فئة فرعية لكل تركيبة:
protocol Coffee { func cost() -> Int; func label() -> String }
struct Espresso: Coffee { func cost() -> Int { 200 }; func label() -> String { "espresso" } }
struct WithMilk: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 50 }
func label() -> String { base.label() + " + milk" }
}
struct WithSyrup: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 30 }
func label() -> String { base.label() + " + syrup" }
}
let fancy = WithSyrup(base: WithMilk(base: WithMilk(base: Espresso())))
print(fancy.label())
print(fancy.cost())الناتج:
espresso + milk + milk + syrup
330تُلائم المُزيّنات الإضافات الشاملة مثل التسجيل المؤقت أو التخزين المؤقت أو التحقّق حول مكوّن موجود، مع بقائه دون تغيير:
protocol DataSource { func fetch(_ id: Int) -> String }
struct Database: DataSource { func fetch(_ id: Int) -> String { "row \(id)" } }
struct Logged: DataSource {
let inner: any DataSource
func fetch(_ id: Int) -> String {
print("fetching \(id)")
return inner.fetch(id)
}
}
let source: any DataSource = Logged(inner: Database())
print(source.fetch(7))الناتج:
fetching 7
row 7الكود الذي يستخدم الكائن لا يرى سوى البروتوكول، لذلك لا يمكنه التمييز بين كائن مُزيَّن وكائن عادي. وهذا ما يجعل إضافة هذا النمط لاحقًا آمنة.
func printReceipt(_ c: any Coffee) { // يعمل مع Espresso وأي نسخة ملفوفة
print("\(c.label()): \(c.cost())")
}التحدي
سهلتُبنى البيتزا باستخدام المُزخرفات. يحتوي البروتوكول Pizza على price() وtoppings() (مصفوفة من الأسماء). تكون Base بسعر 800 ومن دون إضافات. نفّذ المُزخرفات Cheese (+150) وOlives (+100) وDouble (يضاعف سعر كل ما بداخله ويكرر إضافاته مرتين). أكمل build(_:)، الذي يبدأ من Base ويغلّفه بمُزخرف واحد لكل اسم بالترتيب.
يقرأ الكود المُ provided أسطرًا تحتوي على أسماء إضافات مفصولة بفواصل (أو سطرًا فارغًا)، ويبني كل بيتزا ويطبع 1050: cheese, olives أو 800: plain.
يجب أن تضع كودك في Pizza.swift وBase.swift وCheese.swift وOlives.swift وDouble.swift وFunctions.swift. يحتوي main.swift على كود الإدخال/الإخراج المُ provided ولا يمكن تعديله.
جرّب بنفسك
// كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
let names = line.isEmpty ? [] : line.split(separator: ",").map(String.init)
let p = build(names)
let tops = p.toppings()
print("\(p.price()): " + (tops.isEmpty ? "plain" : tops.joined(separator: ", ")))
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الأصناف والكائنات
العمل مع الملفاتتعريف الأصنافالأساليب وselfالمُهيِّئاتالأصناف هي مراجعمراجعة - بطاقة المكتبة4التغليف
التحكم في الوصولللقراءة فقط من الخارجحماية الحالةالمهيئات القابلة للفشلمراجعة - الحساب البنكي7تعدد الأشكال والأنواع العامة
تعدد الأشكالتحويل الأنواعالدوال العامةالأنواع العامةمراجعة - حاسبة الأشكالتدرّب بنفسك: Playground لـ Swift