نمط البناء
جزء من قسم البرمجة كائنية التوجه في رحلة Swift على Coddy. الدرس 48 من 57.
يجمع نمط الباني كائنًا معقدًا خطوةً بخطوة. تضبط كل خطوة جزءًا واحدًا وتعيد الباني، بحيث يمكن تسلسل الخطوات، ثم ينشئ build() النهائي الكائن المكتمل:
struct Request {
let url: String
let method: String
let headers: [String: String]
}
struct RequestBuilder {
private var url = ""
private var method = "GET"
private var headers: [String: String] = [:]
func url(_ u: String) -> RequestBuilder { var b = self; b.url = u; return b }
func method(_ m: String) -> RequestBuilder { var b = self; b.method = m; return b }
func header(_ k: String, _ v: String) -> RequestBuilder { var b = self; b.headers[k] = v; return b }
func build() -> Request { Request(url: url, method: method, headers: headers) }
}
let req = RequestBuilder().url("/users").method("POST").header("Auth", "token").build()
print("\(req.method) \(req.url) \(req.headers.count) header")الناتج:
POST /users 1 headerيمكن للمنشئ التحقق من الإعداد بالكامل قبل إنشاء الكائن، لذا لا يتم إنشاء كائن غير صالح أبدًا. هنا ترمي build() خطأً عند فقدان جزء مطلوب:
enum BuildError: Error { case missingURL }
struct Request { let url: String; let method: String }
struct RequestBuilder {
private var url: String?
private var method = "GET"
func url(_ u: String) -> RequestBuilder { var b = self; b.url = u; return b }
func method(_ m: String) -> RequestBuilder { var b = self; b.method = m; return b }
func build() throws -> Request {
guard let url else { throw BuildError.missingURL }
return Request(url: url, method: method)
}
}
do {
_ = try RequestBuilder().method("PUT").build()
} catch {
print("cannot build: \(error)")
}الناتج:
cannot build: missingURLتنسخ كل خطوة البنية وتغيّر النسخة، لذا يمكن إعادة استخدام منشئ تم تكوينه جزئيًا كقالب لعدة كائنات:
struct PizzaBuilder {
private var size = "medium"
private var toppings: [String] = []
func size(_ s: String) -> PizzaBuilder { var b = self; b.size = s; return b }
func add(_ t: String) -> PizzaBuilder { var b = self; b.toppings.append(t); return b }
func build() -> String { "\(size) pizza with " + (toppings.isEmpty ? "nothing" : toppings.joined(separator: ", ")) }
}
let base = PizzaBuilder().size("large").add("cheese")
print(base.add("ham").build())
print(base.add("olives").build())الناتج:
large pizza with cheese, ham
large pizza with cheese, olivesتغطي القيم الافتراضية للمعاملات في Swift الحالات البسيطة بالفعل (Request(url: "/", method: "GET")). يصبح نمط البناء مفيدًا عندما يحتوي كائن على أجزاء اختيارية كثيرة، أو أجزاء تعتمد على بعضها، أو قواعد يجب التحقق منها قبل أن يكون موجودًا.
// بسيط: الوسائط الافتراضية كافية
struct Point { var x = 0, y = 0 }
// معقد: أجزاء اختيارية كثيرة والتحقق -> builder
let report = ReportBuilder().title("Q3").add(section: s1).add(section: s2).footer("draft").build()التحدي
سهلاكتب منشئًا لرسائل البريد الإلكتروني. يحتوي EmailBuilder على خطوات قابلة للتسلسل هي to(_:) (يمكن استدعاؤها عدة مرات لإضافة مستلمين)، وsubject(_:)، وbody(_:)، وurgent(). تُرجع build() قيمة من النوع Email أو ترمي الخطأ EmailError.noRecipients أو EmailError.noSubject. يضيف البريد الإلكتروني العاجل [URGENT] أمام موضوعه.
يقرأ الكود المقدم الأوامر لكل رسالة بريد إلكتروني، بحيث تفصل بينها أسطر --- (مثل to ada@x.io وsubject Hello وbody text وurgent ثم send)، ويطبع To: ada@x.io, bo@x.io | [URGENT] Hello | text أو error: no recipients أو error: no subject.
ضع الكود الخاص بك في Email.swift وEmailError.swift وEmailBuilder.swift. يحتوي main.swift على كود الإدخال/الإخراج المقدم، ولا يمكن تعديله.
جرّب بنفسك
// كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
var input: [String] = []
while let line = readLine() { input.append(line) }
var builder = EmailBuilder()
for line in input {
let p = line.split(separator: " ", maxSplits: 1).map(String.init)
switch p[0] {
case "to": builder = builder.to(p[1])
case "subject": builder = builder.subject(p[1])
case "body": builder = builder.body(p[1])
case "urgent": builder = builder.urgent()
case "send":
do {
let e = try builder.build()
print("To: \(e.recipients.joined(separator: ", ")) | \(e.subject) | \(e.body)")
} catch EmailError.noRecipients {
print("error: no recipients")
} catch {
print("error: no subject")
}
default: builder = EmailBuilder()
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الأصناف والكائنات
العمل مع الملفاتتعريف الأصنافالأساليب وselfالمُهيِّئاتالأصناف هي مراجعمراجعة - بطاقة المكتبة4التغليف
التحكم في الوصولللقراءة فقط من الخارجحماية الحالةالمهيئات القابلة للفشلمراجعة - الحساب البنكي7تعدد الأشكال والأنواع العامة
تعدد الأشكالتحويل الأنواعالدوال العامةالأنواع العامةمراجعة - حاسبة الأشكالتدرّب بنفسك: Playground لـ Swift