Table of Contents
وتشكل خطة البناء أحد أكثر أنماط التصميم عملية لإدارة التعقيد أثناء إنشاء الجسم، وفي سويفت، حيث تتميز السلامة والقابلية للقراءة، توفر خطة البناء طريقة نظيفة ومتسلسلة لبناء أشياء تتطلب خيارات تشكيلية كثيرة، أو معايير اختيارية، أو عوامل معقدة مشتركة، وتستكشف هذه المادة النمط بعمق، من التنفيذ الأساسي إلى أوجه التباين المتقدمة، وتقدم أمثلة على العالم السوفييتي يمكن أن تطبقها.
فهم خطة البناء
وبدلا من إجبار مبدئي ضخم على استخدام عشرات البارامترات - التي قد يكون الكثير منها اختياريا أو قيما ناقصة - تخلقون جسما مستقلا من بنيان يجمع خطوة التكوين خطوة، وعندما يتم تحديد جميع الممتلكات المرغوبة، تدعون أسلوبا لإنتاج المنتج النهائي، الذي كثيرا ما يكون غير قابل للآمال.
وهذا النمط له قيمة خاصة في سويفت عند التعامل مع:
- عناصر الأشعة فوق البنفسجية (النظريات، الخلايا، الطبقات) مع العديد من خيارات المظهر
- تشكيلات طلبات الشبكة (الرؤوس، والبارامترات، والتوثيق)
- مواد نموذجية أساسية للبيانات أو العقارات ذات علاقات اختيارية
- الأجسام الرئيسية التي تتطلب التصديق قبل الإنشاء
إن خطة البناء تعود إلى الأسرة الإبداعية، وغالبا ما تقارن بمنهجية المصانع والأنماط المصانعية المجردة، غير أن البنايين فريدون من حيث أنهم يسمحون لنفس عملية البناء بخلق أشكال مختلفة - ويمكنكم إعادة استخدام البنين في تشكيلات متعددة دون تغيير واجهة الوصل بينه وبينه.
تنفيذ خطة البناء في سويفت: نموذج المؤسسة
فلنبدأ بمثال محدد على السويت، تخيل أنك تحتاج إلى طبقة فرعية من العرف (]) مع عدة ممتلكات قابلة للتشكيل، وبدون بناية، قد ينتهي بك المطاف مع مبدئي طويل أو طريقة تركيب على الممتلكات، ومع البنين يصبح الرمز مستندا ذاتيا وصريحا.
الخطوة 1: تعريف المنتج
المنتج هو الشيء الذي تريد أن تخلقه في نهاية المطاف.
struct CustomViewConfig {
let backgroundColor: UIColor
let cornerRadius: CGFloat
let borderWidth: CGFloat
let borderColor: UIColor
let shadowOpacity: Float
let shadowRadius: CGFloat
}
الخطوة 2: إنشاء المبنى
ويحمل المبني قيماً غير مباشرة لكل ممتلكات ويقدم أساليب تستكملها، ويعود عادة (أو نوع البناء) لتمكين السلاسل المنهجية.
class CustomViewConfigBuilder {
private var backgroundColor: UIColor = .white
private var cornerRadius: CGFloat = 0.0
private var borderWidth: CGFloat = 0.0
private var borderColor: UIColor = .clear
private var shadowOpacity: Float = 0.0
private var shadowRadius: CGFloat = 0.0
@discardableResult
func withBackgroundColor(_ color: UIColor) -> Self {
self.backgroundColor = color
return self
}
@discardableResult
func withCornerRadius(_ radius: CGFloat) -> Self {
self.cornerRadius = radius
return self
}
@discardableResult
func withBorder(width: CGFloat, color: UIColor) -> Self {
self.borderWidth = width
self.borderColor = color
return self
}
@discardableResult
func withShadow(opacity: Float, radius: CGFloat) -> Self {
self.shadowOpacity = opacity
self.shadowRadius = radius
return self
}
func build() -> CustomViewConfig {
// optional validation can go here
return CustomViewConfig(
backgroundColor: backgroundColor,
cornerRadius: cornerRadius,
borderWidth: borderWidth,
borderColor: borderColor,
shadowOpacity: shadowOpacity,
shadowRadius: shadowRadius
)
}
}
Notice the use of — this allows callers to ignore the return value if they don’t need chaining, which can be useful in some contexts.
الخطوة 3: استخدام البناية
let config = CustomViewConfigBuilder()
.withBackgroundColor(.systemBlue)
.withCornerRadius(12.0)
.withBorder(width: 1.5, color: .darkGray)
.withShadow(opacity: 0.3, radius: 4.0)
.build()
// Apply the config to a view
let myView = UIView()
myView.backgroundColor = config.backgroundColor
myView.layer.cornerRadius = config.cornerRadius
myView.layer.borderWidth = config.borderWidth
myView.layer.borderColor = config.borderColor.cgColor
myView.layer.shadowOpacity = config.shadowOpacity
myView.layer.shadowRadius = config.shadowRadius
متى تستخدمين جهاز البناء
The builder pattern shines in the following scenarios:
- Many optional parameters:] If a type has more than 3-4 form options, a builder improves readability and reduces error-prone positional arguments.
- Immutability requirements:] You want the final object to be immutable, but its construction requires many steps where middle state matters.
- Compplex validation:] The method can validate all inputs and throw errors if something is invalid, preventing broken objects from being created.
- تريد واجهة "مؤثرة" أو "مقبولة" تقرأ مثل اللغة الطبيعية
- Cros-cutting concerns:] When construction logical is reused across multiple places in a codebase, the builder centralizes that logical.
غير أن البنايين ليسوا دائما الخيار الصحيح، فبالنسبة للأشياء البسيطة التي لها ممتلكات قليلة، كثيرا ما يكون المبدئي الذي له قيم غير مقصودة كافيا، وبالنسبة للأجسام التي لا تتطلب تشكيلا يتجاوز الأساسيات، يضيف البنّاء رأسا زائدا غير ضروري.
فرق نمط البناء
وهناك عدة تغييرات مشتركة في نمط البناء المستخدم في مشاريع سويفت:
1 - البناء الكلاسيكي
وكما هو مبين أعلاه، فإن طبقة منفصلة تُحوز الدولة وتعيد منتجاً، وهذا هو أكثر مرونة ويتيح التحقق من المجازفة المعقدة والتشكيل المنطقي.
2 - البناء مع الهيكل (نوع القرية)
وبما أن سويفت تشجع على أنواع القيمة، فيمكنك تنفيذ البناء كغش، إلا أن سلاسل الطرق تتطلب أساليب متغيرة، مما يعني أنه يجب أن تُعتبر المهام [(FLT:9]) أو أن تعيد نسخة جديدة من هذا النظام، فالنهج الأخير أكثر فعالية ولكنه يمكن أن يكون أقل كفاءة بالنسبة للعديد من المهام.
struct CustomViewConfigBuilder {
private var backgroundColor: UIColor = .white
// ...
func withBackgroundColor(_ color: UIColor) -> CustomViewConfigBuilder {
var copy = self
copy.backgroundColor = color
return copy
}
func build() -> CustomViewConfig {
return CustomViewConfig(backgroundColor: backgroundColor, ...)
}
}
3 - بناء النتائج (المشروع 5-4+)
بناء نتائج (سويفت) (يسمى أيضاً بناة الوظائف) يقدمون سلسلة توضيحية ذات صلة نظرياً بنمط البناء، في حين أنه لا بديل مباشر، يمكن استخدام بناة النتائج لبناء أجسام معقدة في لغة محددة، على سبيل المثال، إنّ (سويفت أوي) هو أحد مبنيّ النتائج المعروف جيداً.
مثال: بناء النتائج العرفية لأغراض التداول
@resultBuilder
struct ViewConfigBuilder {
static func buildBlock(_ components: ViewConfigComponent...) -> [ViewConfigComponent] {
return components
}
}
protocol ViewConfigComponent {
func apply(to builder: CustomViewConfigBuilder)
}
struct BackgroundColorComponent: ViewConfigComponent {
let color: UIColor
func apply(to builder: CustomViewConfigBuilder) {
builder.withBackgroundColor(color)
}
}
// Usage with @ViewConfigBuilder
let config = ViewConfigBuilder.buildBlock(
BackgroundColorComponent(color: .red),
CornerRadiusComponent(radius: 8)
)
هذا النهج أكثر تقدماً وأفضل محجوزة لـ دي إس إل أو عندما تريد إنفاذ نظام محدد من التشكيلات
Real-World Use Cases in iOS Development
الشبكة
وكثيرا ما تحتاج المكتبات الشبكية إلى بناء طلبات تتضمن معايير اختيارية كثيرة: الاتحاد الثوري الوطني لكولومبيا، وأسلوب الحزب الديمقراطي لكوت ديفوار، والرؤساء، والجسد، والبارامترات، وسياسة الاختبار، والتوقيت، وما إلى ذلك.
class APIRequestBuilder {
private var url: URL
private var method: String = "GET"
private var headers: [String: String] = [:]
private var body: Data?
private var queryItems: [URLQueryItem] = []
init(url: URL) {
self.url = url
}
func setMethod(_ method: String) -> Self {
self.method = method
return self
}
func addHeader(key: String, value: String) -> Self {
headers[key] = value
return self
}
func setBody(_ data: Data) -> Self {
self.body = data
return self
}
func addQueryItem(name: String, value: String) -> Self {
queryItems.append(URLQueryItem(name: name, value: value))
return self
}
func build() -> URLRequest {
var request = URLRequest(url: url)
request.httpMethod = method
request.allHTTPHeaderFields = headers
request.httpBody = body
if var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
components.queryItems = queryItems
request.url = components.url
}
return request
}
}
// Usage
let request = APIRequestBuilder(url: URL(string: "https://api.example.com/users")!)
.setMethod("POST")
.addHeader(key: "Content-Type", value: "application/json")
.setBody(try! JSONEncoder().encode(userData))
.build()
هيئة البيانات الأساسية
فالأجسام الأساسية التي تدار البيانات هي أشياء سيئة السمعة لتخلقها، ويمكن للبنّاء أن يلخص البحث عن الممتلكات وإحالتها.
class UserEntityBuilder {
private let context: NSManagedObjectContext
private var name: String = ""
private var email: String = ""
private var age: Int = 0
init(context: NSManagedObjectContext) {
self.context = context
}
func withName(_ name: String) -> Self {
self.name = name
return self
}
func withEmail(_ email: String) -> Self {
self.email = email
return self
}
func withAge(_ age: Int) -> Self {
self.age = age
return self
}
func build() -> User {
let user = NSEntityDescription.insertNewObject(forEntityName: "User", into: context) as! User
user.name = name
user.email = email
user.age = Int32(age)
return user
}
}
معالجة الخطأ في البناء
وفي بعض الأحيان، ينبغي أن يفشل إنشاء الجسم إذا كانت التشكيلة غير صحيحة، ويمكن أن يكون أسلوب ] هو الرمي، وهو أسلوب نظيف لإنفاذ القواعد.
struct LoginConfig {
let username: String
let password: String
let serverURL: URL
}
class LoginConfigBuilder {
private var username: String?
private var password: String?
private var serverURL: URL?
func withUsername(_ username: String) -> Self {
self.username = username
return self
}
func withPassword(_ password: String) -> Self {
self.password = password
return self
}
func withServerURL(_ url: URL) -> Self {
self.serverURL = url
return self
}
func build() throws -> LoginConfig {
guard let username = username, !username.isEmpty else {
throw BuilderError.missingUsername
}
guard let password = password, password.count >= 8 else {
throw BuilderError.invalidPassword
}
guard let serverURL = serverURL else {
throw BuilderError.missingServerURL
}
return LoginConfig(username: username, password: password, serverURL: serverURL)
}
}
enum BuilderError: Error {
case missingUsername
case invalidPassword
case missingServerURL
}
// Usage
do {
let config = try LoginConfigBuilder()
.withUsername("jdoe")
.withPassword("secret1234")
.withServerURL(URL(string: "https://auth.example.com")!)
.build()
} catch {
print("Failed to build login config: \(error)")
}
اعتبارات الأداء
البنايات في سويفت عادة خفيفة الوزن، ولكن هناك بعض الأشياء التي لا يغيب عن بالها:
- Memory overhead:] Each builder instance holds a copy of all properties until is called. For large formations, consider using a struct builder that creates a copy only on mutation (the functional approach).
- Method chaining:] each call returns the same builder instance (for classes) or a new copy (for structs).
- Validation cost:] If validation in ] is expensive, consider caching or postponedring it, or providing a light weight validation method that can be called earlier.
- Usese in cycles:] If you need to create many similar objects, avoid recreating the builder from scrap each time. instead, reuse a builder and reset its state after each .
Comparison: Builder vs. Factory vs. Direct Initializer
| Approach | Best For | Downside |
|---|---|---|
| Direct Initializer | Simple objects with few required parameters | Becomes unreadable with many optional parameters |
| Factory Method | Subclass selection or logic-based creation | Does not handle step-by-step configuration |
| Builder Pattern | Complex, configurable, and potentially immutable objects | More boilerplate; not suitable for trivial objects |
البنّاء مكمّل للمصانع، يمكنك أن تُعيد مصنعاً يعيد بناة مُحكمة، ثمّ تدع المُتصل يُعدّها أكثر.
التكامل مع نظام سويفتوئي وكامبين
البنايات مناسبة طبيعية لأسلوب (سويفتو) اللامع يمكنك أن تخلق بناً يبني
struct CardViewConfig {
let title: String
let subtitle: String
let iconName: String
let backgroundColor: Color
let tapAction: () -> Void
}
class CardViewConfigBuilder {
private var title: String = ""
private var subtitle: String = ""
private var iconName: String = "star"
private var backgroundColor: Color = .white
private var tapAction: (() -> Void)? = nil
func withTitle(_ title: String) -> Self {
self.title = title
return self
}
func withSubtitle(_ subtitle: String) -> Self {
self.subtitle = subtitle
return self
}
func withIcon(_ name: String) -> Self {
self.iconName = name
return self
}
func withBackground(_ color: Color) -> Self {
self.backgroundColor = color
return self
}
func withTapAction(_ action: @escaping () -> Void) -> Self {
self.tapAction = action
return self
}
func build() -> CardViewConfig {
return CardViewConfig(
title: title,
subtitle: subtitle,
iconName: iconName,
backgroundColor: backgroundColor,
tapAction: tapAction ?? {}
)
}
}
// Usage in a SwiftUI view
struct ContentView: View {
var body: some View {
let config = CardViewConfigBuilder()
.withTitle("Welcome")
.withSubtitle("Get started with our app")
.withIcon("hand.wave")
.withBackground(.blue.opacity(0.1))
.withTapAction { print("Tapped!") }
.build()
CardView(config: config)
}
}
struct CardView: View {
let config: CardViewConfig
var body: some View {
VStack {
Image(systemName: config.iconName)
.font(.largeTitle)
Text(config.title)
.font(.headline)
Text(config.subtitle)
.font(.subheadline)
}
.padding()
.background(config.backgroundColor)
.cornerRadius(10)
.onTapGesture(perform: config.tapAction)
}
}
الشلالات المشتركة وكيفية تجنبها
- Forgetting to return self:] Ensure each setter method returns the builder type. Use if you want to allow callers to ignore the return.
- Mutable shared state:] If your builder is used across threads, add thread safety (e.g., use a private chain queue or copy-on-write semantics).
- Over-engineering:] لا تطبق نمط البناء على كل جسم، وإذا كان جسمك يملك فقط عقارين أو ثلاث خصائص، فإن المبدعة الأولية البسيطة التي لها قيم غير مباشرة هي أكثر وضوحا وتتطلب قدرا أقل من الشفرة.
- Missing validation:] Builders that do not validate in ] can produce objects in an invalid state. always check assumptions at the earliest safe point.
- Inconsistent method naming:] Use a consistent prefix like or ] to make the API recognizable. Some teams prefer , , etc.
الموارد الخارجية
- Builder Pattern on Wikipedia]
- وثائق آبل بشأن بناء التلقيح
- The Builder Pattern in Swift by John Sundell]
- Builder Pattern on Refactoring Guru]
خاتمة
إن خطة البناء أداة قوية في أي ترسانة من مطوري سويفت للتعامل مع عملية الاستبدالية المعقدة، مع الوضوح، والقابلية للاستمرار، والسلامة، وبفصل منطق البناء عن المنتج النهائي، يمكن أن تخلق مؤشرات سريعة يسهل استخدامها ويصعب إساءة استخدامها، وسواء كنت تصوغ آراء، أو بناء طلبات الشبكة، أو بناء نماذج للمجالات، فإن نمط البناء يساعدك على إبقاء رموزك نظيفة ومواضيعك.