04. Builder va Prototype: Telescoping Constructor Fojiasi
🏛️ iOS Design Patterns — Mukammal Enterprise & Architecture Qo‘llanma
04. Creational: Builder va Prototype Pattern
"Agar sizning konstruktoringizda 12 ta parametr bo'lib, ulardan 8 tasi ixtiyoriy (optional) bo'lsa — siz 'Telescoping Constructor' balosiga duchor bo'lgansiz. Builder bu boshog'riqni chiroyli va o'qishli oqimga aylantiradi."
1. Builder Pattern Nima va U Qaysi Muammoni Hal Qiladi?
Builder — bu ko'p bosqichli va ko'p parametrli murakkab obyektlarni bosqichma-bosqich, zanjirband uslubda (Method Chaining / Fluent API) yaratish imkonini beruvchi pattern.
"Telescoping Constructor" Fojiasi:
// ❌ QO'RFINCHLI ANTI-PATTERN:
let request = URLRequest(
url: url,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 60,
mainDocumentURL: nil,
networkServiceType: .default,
allowsCellularAccess: true,
allowsExpensiveNetworkAccess: true,
allowsConstrainedNetworkAccess: true,
httpMethod: "POST",
allHTTPHeaderFields: ["Authorization": "Bearer ..."],
httpBody: bodyData
)
Bu kodni o'qish ham, yangi parametr qo'shish ham o'ta noqulay.
flowchart LR
Start["Builder yaratish:\nURLRequestBuilder()"] --> SetURL[".setURL('/api/orders')"]
SetURL --> SetMethod[".setMethod(.post)"]
SetMethod --> SetAuth[".addBearerToken(token)"]
SetAuth --> SetBody[".setJSONBody(payload)"]
SetBody --> Build[".build() -> URLRequest"]
style Start fill:#131720,stroke:#7da2ce,stroke-width:1px
style Build fill:#131720,stroke:#bef264,stroke-width:2px
2. Real-World Swift 6 Kodi: Enterprise URLRequestBuilder
import Foundation
public enum HTTPMethod: String {
case get = "GET"
case post = "POST"
case put = "PUT"
case delete = "DELETE"
}
public final class URLRequestBuilder {
private var baseURL: URL
private var path: String = ""
private var method: HTTPMethod = .get
private var headers: [String: String] = [:]
private var queryParams: [String: String] = [:]
private var body: Data?
private var timeout: TimeInterval = 30.0
public init(baseURL: URL) {
self.baseURL = baseURL
}
@discardableResult
public func setPath(_ path: String) -> Self {
self.path = path
return self
}
@discardableResult
public func setMethod(_ method: HTTPMethod) -> Self {
self.method = method
return self
}
@discardableResult
public func addHeader(key: String, value: String) -> Self {
self.headers[key] = value
return self
}
@discardableResult
public func addBearerToken(_ token: String) -> Self {
self.headers["Authorization"] = "Bearer \(token)"
return self
}
@discardableResult
public func addQueryParam(key: String, value: String) -> Self {
self.queryParams[key] = value
return self
}
@discardableResult
public func setJSONBody<T: Encodable>(_ object: T) throws -> Self {
self.body = try JSONEncoder().encode(object)
self.headers["Content-Type"] = "application/json"
return self
}
public func build() throws -> URLRequest {
var fullURL = baseURL.appendingPathComponent(path)
if !queryParams.isEmpty {
var components = URLComponents(url: fullURL, resolvingAgainstBaseURL: false)
components?.queryItems = queryParams.map { URLQueryItem(name: $0.key, value: $0.value) }
if let configuredURL = components?.url {
fullURL = configuredURL
}
}
var request = URLRequest(url: fullURL, timeoutInterval: timeout)
request.httpMethod = method.rawValue
request.httpBody = body
for (key, value) in headers {
request.addValue(value, forHTTPHeaderField: key)
}
return request
}
}
Ishlatilishi (Toza va O'qishli):
let request = try URLRequestBuilder(baseURL: URL(string: "https://api.azimjondev.uz")!)
.setPath("/v1/orders/create")
.setMethod(.post)
.addBearerToken("jwt_secret_token_123")
.addQueryParam(key: "currency", value: "UZS")
.setJSONBody(OrderPayload(itemsCount: 3))
.build()
3. Swift'dagi @resultBuilder: SwiftUI Qanday Qilib Builderni Tilga Kiritdi?
SwiftUI dagi har bir VStack, HStack yoki List aslida Swift'ning rasmiy Result Builder imkoniyatiga asoslangan:
@resultBuilder
public struct ViewBuilder { ... }
Siz quyidagicha yozganingizda:
VStack {
Text("Sarlavha")
Button("Bosish") {}
}
Swift kompilyatori orqa fonda ViewBuilder.buildBlock(Text(...), Button(...)) chaqiruvini bajaradi. Bu Builder patternining zamonaviy deklarativ shaklidir!
4. Prototype Pattern: Nusxa Olish va Copy-on-Write (CoW)
Prototype — bu mavjud obyektning aniq nusxasini (klonini) yaratishga imkon beruvchi pattern.
Shallow Copy vs Deep Copy Fojiasi:
flowchart TD
subgraph Shallow["❌ Shallow Copy (Sayoz Nusxa)"]
ObjA["Obyekt A"] --> Ref["Bir Xil Xotira Manzili (Shared Reference)"]
ObjB["Obyekt B"] --> Ref
ObjB -.->|"B o'zgarsa"| Corrupt["💥 A ham kutilmaganda o'zgarib ketadi!"]
end
subgraph Deep["✅ Deep Copy (Chuqur Nusxa)"]
Obj1["Obyekt A"] --> Ref1["RAM Manzil #1"]
Obj2["Obyekt B (Klon)"] --> Ref2["Yangi RAM Manzil #2"]
Obj2 -.->|"B o'zgarsa"| Safe["A xavfsiz va o'zgarmas qoladi!"]
end
style Shallow fill:#251014,stroke:#f87171,stroke-width:1px
style Deep fill:#0d1f14,stroke:#4ade80,stroke-width:1px
Swift'da Prototype:
- Structlar (Value Types): Swift structlari tabiiy ravishda Prototype patternini o'z ichiga oladi:
struct UserProfile { var name: String }
var user1 = UserProfile(name: "Ali")
var user2 = user1 // Avtomatik ravishda to'liq mustaqil klon! (Copy-on-Write)
user2.name = "Vali" // user1 "Ali" bo'lib qoladi
- Classlar (Reference Types): Classlar bilan ishlaganda esa
NSCopyingprotokolini amalga oshirish kerak bo'ladi:
final class ComplexReport: NSCopying {
var title: String
var metrics: [String: Int]
init(title: String, metrics: [String: Int]) {
self.title = title
self.metrics = metrics
}
// Deep Copy implementatsiyasi
func copy(with zone: NSZone? = nil) -> Any {
return ComplexReport(title: self.title, metrics: self.metrics)
}
}
5. Interaktiv Savol-Javob (Self-Check Quiz)
<details> <summary><b>1-Savol: Nega Swift'da Builder metodlarida '@discardableResult' atributi yoziladi?</b> (Yechimni ko'rish uchun bosing)</summary>
Tahlil va Yechim: Metod return self qaytargani sababli, agar chaqiruvchi ushbu qaytgan qiymatni alohida o'zgaruvchiga o'zlashtirmasa (masalan zanjir o'rtasida), Swift kompilyatori Result of call to '...' is unused degan ogohlantirish (warning) bermasligi uchun @discardableResult qo'yiladi. </details>
<details> <summary><b>2-Savol: Qachon oddiy Struct copy o'rniga Class uchun Prototype patternini maxsus yozish talab etiladi?</b> (Yechimni ko'rish uchun bosing)</summary>
Tahlil va Yechim: Agar obyekt juda og'ir bo'lsa (masalan, grafika chizish loyihalarida millionlab poligonlardan iborat 3D model yoki rasm tahrirlagichdagi ko'p qatlamli Layer) va uni noldan qayta hisoblab yaratish juda ko'p CPU talab qilsa. Bunday holatda xotiradagi tayyor obyektni klonlab, faqat o'zgargan bir nechta xususiyatini almashtirish 100x tezroq bo'ladi. </details>
6. Amaliy Topshiriq (Senior Challenge)
PushNotificationPayloadBuilder sinfini yarating:
.setTitle(_:),.setBody(_:),.setCategory(_:),.setBadgeCount(_:),.addCustomData(key:value:)metodlariga ega bo'lsin..build()metodi kamidatitlevabodykiritilganini tekshirsin, agar kiritilmagan bo'lsaNotificationBuildError.missingRequiredFieldsxatosini tashlasin (throw qilsin)!