Table of Contents
תבנית ה Builder עומדת כאחת מתבניות העיצוב המעשיות ביותר לניהול המורכבות במהלך יצירת האובייקט.ב- Swift, שבו בטיחות וקריאה מסוגים מוערכים, התבנית המארגנת מציעה דרך נקייה, שרשרתית לבניית אובייקטים הדורשים אפשרויות תצורה רבות, פרמטרים אופציונליים, או תלות הדדית מורכבת. מאמר זה חוקר את התבנית לעומק, החל יישום בסיסי ועד לריאציות מתקדמות, ומספק דוגמאות בעולם האמיתי אתה יכול ליישם פרויקטים משלך.
הבנת התבנית הבונה
התבנית הבונה מפרידה את בנייתו של אובייקט מורכב מהייצוג הסופי שלו.במקום לכפות ראשוני מסיבי עם עשרות פרמטרים - שרבים מהם עשויים להיות אופציונליים או שיש להם ערכי ברירת מחדל - אתה יוצר אובייקט בונה נפרד אשר אוסף את התצורה צעד אחר צעד.כאשר כל התכונות הרצויות מוגדרות, אתה קורא FLT:0 שיטה לייצור הסופי, לעתים קרובות לא מסול, מוצר.
דפוס זה הוא בעל ערך במיוחד ב Swift כאשר הוא מתמודד:
- רכיבים UI (מראות, תאים, שכבות) עם אפשרויות מראה רבות
- דרישות רשת (ראשיים, פרמטרים, אימות)
- מידע מקורי או אובייקטים מודל אמיתיים עם מערכות יחסים אופציונליות
- אובייקטים דומיין הדורשים אימות לפני הבריאה
התבנית המארגנת שייכת למשפחה הבריאה, ולעיתים קרובות בהשוואה לתבניות של המפעל והמפעלים המפוכחים.עם זאת, בני הבנאים הם ייחודיים בכך שהם מאפשרים לאותו תהליך בנייה ליצור ייצוגים שונים – ניתן להשתמש בבנונית לתצורה מרובות מבלי לשנות את ממשקה.
יישום התבנית הלומדת ב Swift: דוגמא של הקרן
בואו נתחיל עם דוגמא קונקרטית סוויפט.דמיין שאתה צריך מחלקה אישית (FLT:1) עם כמה תכונות תצורה.ללא בונה, אתה יכול בסופו של דבר עם ראשוניז ארוך או שיטת ההתקנה של רכוש-לבד.עם הבונים, הקוד הופך להיות דיקור עצמי ומבטא.
שלב 1: Define the Product
המוצר הוא האובייקט שאתה בסופו של דבר רוצה ליצור.ב Swift, זה נפוץ להשתמש ב-FLT:2 עבור סימנטיקה ערך ולהפוך אותו לבלתי-משתנה לאחר הבנייה.
struct CustomViewConfig {
let backgroundColor: UIColor
let cornerRadius: CGFloat
let borderWidth: CGFloat
let borderColor: UIColor
let shadowOpacity: Float
let shadowRadius: CGFloat
}
שלב 2: יצירת הממציא
הבונים מחזיקים בערכי ברירת מחדל עבור כל נכס ומספק שיטות המעדינות אותם, בדרך כלל חוזרים (FLT:4) כדי לאפשר שרשראות שיטה.
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
)
}
}
שימו לב לשימוש ב-(FLT:6) – זה מאפשר למתקשרים להתעלם משווי ההחזר אם הם לא צריכים שרשראות, אשר יכול להיות שימושי בהקשרים מסוימים.
שלב 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
מתי להשתמש בתבנית הלומדת
תבנית הבונים מאירה בתרחישים הבאים:
- (FLT:0) פרמטרים אופציונליים רבים: אם יש לטיפוס יותר מ-3 אפשרויות תצורה, בונה משפר את יכולת הקריאה ומפחית טענות מקבילות של שגיאה.
- דרישות סודיות:0 (א) 1 (ב) , אתה רוצה שהאובייקט הסופי יהיה בלתי-מוגדר, אך הבנייה דורשת צעדים רבים שבהם המצב ביניים חשוב.
- (ב) ,0) כפלה מורכבת: 1FLT:1 השיטה (FLT:8) יכולה לאמת את כל הקלטים ולזרוק שגיאות אם משהו לא חוקי, למנוע פריטים שבורים מלהיות נברא.
- (ב) ,0) ממשקי API נפרדים: FLT:1, אתה רוצה ממשק "משפיע" או "מחייב" שקורא כמו שפה טבעית.
- (ב) ⁇ :0) , רדיפה אחר דאגות: כאשר לוגיקה בנייה מותקפת על פני מספר מקומות בבסיס קוד, הבונים מרכזי בלוגיקה זו.
עם זאת, בנינים אינם תמיד הבחירה הנכונה. עבור פריטים פשוטים עם מעט נכסים, ראשוניז עם ערכי ברירת מחדל הוא לעתים קרובות מספיק. עבור אובייקטים הדורשים תצורה מעבר ליסוד, בונה מוסיף מיותר.
המונחים: Pattern Variations
ישנם מספר וריאציות נפוצות של דפוס הבונים המשמש בפרויקטים סוויפט:
1. Classic Builder
כפי שמוצג לעיל - מעמד נפרד מחזיק המדינה וחוזר מוצר.זה גמיש ביותר ומאפשר אימות מורכב ולוגיקה ההתקנה.
2.Build with Struct (Value Type)
מאז סוויפט מעודדת סוגים של ערך, אתה יכול ליישם את הבונים כמבנה.עם זאת, שיטת שרשראות דורש שיטות מגרות, כלומר אתה צריך לסמן פונקציות כמו 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.התוצאה היא: Swift 5.4+)
הבונים של סוויפט (נקראים גם בונה פונקציה) לספק סינטקס מפואר הקשור באופן קונספטואלי לדפוס הנבנה.בעוד שלא תחליף ישיר, יבנונים תוצאה ניתן להשתמש כדי לבנות אובייקטים מורכבים בסגנון ספציפי לתחום (DSL) לדוגמה, Swift's FLT:11 הוא בונה תוצאה ידועה.
דוגמה: תוצאות מכס עבור קונריגציה
@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)
)
גישה זו היא מתקדמת יותר וטובה יותר עבור DSLs או כאשר אתה רוצה לאכוף הזמנה מסוימת של תצורה.
מצבי שימוש אמיתיים ב-iOS Development
WEB מבקש סודיות
ספריות רשת צריכות לעיתים קרובות לבנות בקשות עם פרמטרים אופציונליים רבים: כתובת URL, שיטת HTTP, Headers, Body,שאילתות, מדיניות שמפו, זמן, וכו ' בונה מפשט את זה.
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()
המונחים: right data Configuration
אובייקטים מנוהלים על ידי Core נתונים הם למעשה פועלי כדי ליצור, בונה יכול לבודד את ה-FLT:14 ואת הקצאת רכוש.
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
}
}
טעויות ב- Builders
לפעמים יצירת אובייקטים צריכה להיכשל אם התצורה אינה מובנת, שיטת ה-FLT:16 יכולה להיות זריקת, שהיא דרך נקייה לאכוף כללים.
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)")
}
שיקולים
היצרנים ב- Swift הם בדרך כלל קלים, אבל יש כמה דברים שכדאי לזכור:
- (ב) כל אחד מהם מחזיק עותק של כל התכונות עד FLT 18 נקרא: עבור תצורה גדולה, שקול באמצעות בונה מבנה מבנה מבנה שיוצר עותק בלבד על מוטציות (הגישה הפונקציונלית).
- (בשורה:0) ⁇ : (בשורה 1) כל שיחה מחזירה את אותו מקרה בונה (לשיעורים) או עותק חדש (לאורות) בניה המבוססים על הכיתה הם בסדר; בנינים מבני מבנים עשויים לגרום עותקים נוספים, אך המדגם מייעל רבים מאלה שמחוץ להם.
- (ב) [ה]ההתערות: [ה], אם תמהרה [ב] ב[[1924]], היא בעלת ערך, היא נחשבת ל[[המאה ה-19]], או ל[[המאה ה-20]].
- (ב) אם אתה צריך ליצור חפצים דומים רבים, להימנע מתיקון של הבונים מכל פעם.
ההרחבה: Builder vs. Factory vs. Directizer
| 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 |
הבונים הוא משלים למפעלים.You יכול להיות מפעל מחזיר בונה מראש, ואז לתת למתקשר להתאים אותו עוד יותר.
שילוב עם SwiftUI ו-Bloe
הבונים הם התאמה טבעית לסגנון ההבהרת של SwiftUI.You יכול ליצור בונה שבנתה את ה-FLT:21 המבוסס על תצורה.
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)
}
}
מלכודות נפוצות וכיצד להימנע מהם
- (ב) ,0) ,התקבלה: "החלו" (ב) "החלו" (ב) "החלו" (ב"ב) "לבקשו" (ב"ב)" (ב"ב)"ה', "לבקשו מכל מקום אחר" (ב"ב)
- (ב) אם הבונים שלך משמש על פני חוטים, להוסיף בטיחות חוט (למשל, השתמש תור סידורי פרטי או העתק-על-קולי של סמנטיה).
- (ב) [ה]ההנדסה: [ה] [ה]] לא תיישם את תבנית הבונים לכל חפץ.אם לאובייקט שלך יש רק שני או שלושה נכסים, ראשוניז פשוט עם ערכי ברירת מחדל ברור יותר ודורש פחות קוד.
- (ב) ,0) ,העברה: 1 בוראים שאינם מאמתים ב-FLT:24 יכולים לייצר אובייקטים במצב לא חוקי.
- (ב) ,0) ,השם של השיטה: FLT:1 השתמש בתיקון עקבי כמו FLT:25 או FLT:26 כדי להפוך את ה- API לזיהוי.
משאבים חיצוניים
- (ב) ,0) ,364 ספרים על ויקיפדיה
- (ב) ,0) מסמך ה-Apple על תוצאות ה-Norsualsph 1
- (ב) [[1924]]]] [[1924]]]]]]
- (ב) ,0) ,4, בורא עולם (בתרגום חופשי: ⁇ )
מסקנה
תבנית בונה היא כלי חזק ארסנל של כל מפתח Swift לטיפול באובייקט מורכב עם בהירות, שמירה על סוויפט ובטיחות. על ידי הפרדת לוגיקה הבנייה מהמוצר הסופי, אתה יכול ליצור API אקספרסיבי כי הם קלים לשימוש קשה לשימוש לרעה.אם אתה uring תצוגות, בניית בקשות רשת, או בניית מודלים דומיין, תבנית בונה עוזר לך לשמור את הקוד הנקי שלך ואת האובייקטים שלך עם הפשטות שלך, אז למצוא את הערך הקלאסי שלך, עם הנקה, כדי לבנות את הערך שלך עם התקין.