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

نمط Factory

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

يضع نمط المصنع إنشاء الكائنات في مكان واحد. يطلب المستدعون ما يحتاجون إليه، غالبًا بالاسم أو النوع، ويتلقون كائنًا جاهزًا، دون معرفة النوع المحدد الذي تم إنشاؤه:

protocol Shape { func area() -> Double }
struct Square: Shape { let side: Double; func area() -> Double { side * side } }
struct Circle: Shape { let r: Double; func area() -> Double { 3 * r * r } }

enum ShapeFactory {
    static func make(_ kind: String, size: Double) -> (any Shape)? {
        switch kind {
        case "square": return Square(side: size)
        case "circle": return Circle(r: size)
        default: return nil
        }
    }
}

print(ShapeFactory.make("square", size: 3)!.area())
print(ShapeFactory.make("hexagon", size: 3) == nil)

الناتج:

9.0
true

المستدعي يعتمد فقط على البروتوكول. إن إضافة شكل جديد تعني تغيير المصنع، وليس كل مكان ينشئ الأشكال:

protocol Notifier { func send(_ m: String) -> String }
struct Email: Notifier { func send(_ m: String) -> String { "email: \(m)" } }
struct SMS: Notifier { func send(_ m: String) -> String { "sms: \(m)" } }

func makeNotifier(for user: String) -> any Notifier {
    user.hasSuffix("@mail.com") ? Email() : SMS()
}

print(makeNotifier(for: "ada@mail.com").send("hi"))
print(makeNotifier(for: "555-0100").send("hi"))

الناتج:

email: hi
sms: hi

تُعد أساليب المصنع الساكنة على النوع نفسه شكلاً أخف من هذا النمط. فهي تمنح أسماءً للطرق الشائعة لبناء قيمة:

struct Color {
    let r: Int, g: Int, b: Int
    static func gray(_ v: Int) -> Color { Color(r: v, g: v, b: v) }
    static let white = Color(r: 255, g: 255, b: 255)
}

print(Color.gray(100).g)
print(Color.white.b)

الناتج:

100
255

يُعدّ التعداد بدون حالات، مثل ShapeFactory، طريقة شائعة لتجميع دوال المصنع: إذ لا يمكن إنشاء مثيل منه، ولذلك يعمل بحتًا كمساحة أسماء.

enum ShapeFactory {        // لا توجد حالات: لا يمكن إنشاؤه
    static func make(_ kind: String, size: Double) -> (any Shape)? { ... }
}
challenge icon

التحدي

سهل

اكتب مصنعًا لخطط التسعير. يحتوي البروتوكول Plan على name وprice(forUsers:) -> Int. نفّذ Free (بسعر 0، ولعدد يصل إلى 3 مستخدمين فقط: يكون السعر -1 لما يزيد على ذلك، أي غير مسموح)، وTeam (10 لكل مستخدم)، وEnterprise (500 ثابتة بالإضافة إلى 5 لكل مستخدم يتجاوز 100). أكمل PlanFactory.make(_:)، التي تُرجع الخطة للقيم free أو team أو enterprise، وتُرجع nil في الحالات الأخرى.

يقرأ الكود المُزوَّد أسطرًا بالصيغة plan,users ويطبع team for 12 users: 120 أو free does not allow 5 users أو unknown plan: gold.

ضع كودك في Plan.swift وFree.swift وTeam.swift وEnterprise.swift وPlanFactory.swift. يحتوي main.swift على كود الإدخال/الإخراج المُزوَّد، ولا يمكن تعديله.

جرّب بنفسك

// كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    let p = line.split(separator: ",").map(String.init)
    let users = Int(p[1])!
    guard let plan = PlanFactory.make(p[0]) else {
        print("unknown plan: \(p[0])")
        continue
    }
    let price = plan.price(forUsers: users)
    print(price < 0 ? "\(plan.name) does not allow \(users) users" : "\(plan.name) for \(users) users: \(price)")
}
quiz iconاختبر نفسك

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

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

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