06. Decorator va Facade: Logging, Caching va Murakkablikni Yashirish
🏛️ iOS Design Patterns — Mukammal Enterprise & Architecture Qo‘llanma
06. Structural: Decorator va Facade Pattern
"Agar siz har safar yangi xususiyat qo'shish uchun sinfdan meros (Inheritance) olaversangiz, 1 oydan keyin
LoggedInEncryptedCachingNetworkClientdegan dahshatli sinflar iyerarxiyasiga ega bo'lasiz. Decorator va Facade kodni o'zgartirmasdan uni boyitish va soddalashtirish san'atidir."
1. Decorator Pattern: Vorislikka Qarshi Oqilona Yechim
Decorator (Bezakchi) — bu mavjud obyektning tuzilishiga va uning ota sinfiga tegmasdan, unga dinamik ravishda yangi xatti-harakat va mas'uliyatlarni qo'shish imkonini beruvchi pattern.
Vorislik (Inheritance) Nimasi Bilan Yomon?
Agar sizda BasicNetworkClient bo'lsa va unga:
- Kesh kerak bo'lsa ->
CachingNetworkClient - Log yozish kerak bo'lsa ->
LoggingNetworkClient - Ikkalasi ham kerak bo'lsa ->
CachingLoggingNetworkClient - Token qo'shish kerak bo'lsa ->
AuthenticatedCachingLoggingNetworkClient!
Bu sinflar portlashi (Class Explosion) deb ataladi.
flowchart LR
Request["So'rov Jo'natildi"] --> LoggingDec["LoggingDecorator\n(Console'ga yozadi)"]
LoggingDec --> CachingDec["CachingDecorator\n(Keshni tekshiradi)"]
CachingDec --> AuthDec["AuthDecorator\n(Bearer tokenni ulaydi)"]
AuthDec --> CoreClient["CoreURLSessionClient\n(Haqiqiy tarmoqqa chiqadi)"]
style CoreClient fill:#131720,stroke:#bef264,stroke-width:2px
style AuthDec fill:#181d26,stroke:#7da2ce,stroke-width:1px
style CachingDec fill:#181d26,stroke:#7da2ce,stroke-width:1px
style LoggingDec fill:#181d26,stroke:#7da2ce,stroke-width:1px
2. Real Swift 6 Kodi: Tarmoq Klientini Decorator Bilan Bezatish
1. Asosiy Protokol va Baza Klient:
import Foundation
public protocol HTTPClient: Sendable {
func execute(request: URLRequest) async throws -> (Data, HTTPURLResponse)
}
public final class URLSessionHTTPClient: HTTPClient {
private let session: URLSession
public init(session: URLSession = .shared) {
self.session = session
}
public func execute(request: URLRequest) async throws -> (Data, HTTPURLResponse) {
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
return (data, httpResponse)
}
}
2. Logging Decorator (Baza klient kodiga tegilmaydi!):
public final class LoggingHTTPClientDecorator: HTTPClient {
private let decoratee: any HTTPClient
public init(decoratee: any HTTPClient) {
self.decoratee = decoratee
}
public func execute(request: URLRequest) async throws -> (Data, HTTPURLResponse) {
let startTime = CFAbsoluteTimeGetCurrent()
print("🚀 [HTTP Request] \(request.httpMethod ?? "GET") -> \(request.url?.absoluteString ?? "")")
do {
let (data, response) = try await decoratee.execute(request: request)
let duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000
print("✅ [HTTP Response] Status: \(response.statusCode) (\(String(format: "%.1f", duration)) ms), Hajm: \(data.count) bayt")
return (data, response)
} catch {
print("❌ [HTTP Error] \(error.localizedDescription)")
throw error
}
}
}
3. Caching Decorator:
public final class CachingHTTPClientDecorator: HTTPClient {
private let decoratee: any HTTPClient
private let cache = NSCache<NSURL, NSData>()
public init(decoratee: any HTTPClient) {
self.decoratee = decoratee
}
public func execute(request: URLRequest) async throws -> (Data, HTTPURLResponse) {
guard let url = request.url as? NSURL, request.httpMethod == "GET" else {
return try await decoratee.execute(request: request)
}
// Keshni tekshirish
if let cachedData = cache.object(forKey: url) {
print("⚡ [Cache Hit] Keshdan olindi: \(url.absoluteString ?? "")")
let mockResponse = HTTPURLResponse(url: url as URL, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (cachedData as Data, mockResponse)
}
// Asl chaqiruv
let (data, response) = try await decoratee.execute(request: request)
if response.statusCode == 200 {
cache.setObject(data as NSData, forKey: url)
}
return (data, response)
}
}
Ishlatilishi (Matryoshka qo'g'irchog'i kabi yig'iladi):
// Bitta oddiy URLSession klientini Logging va Caching bilan bezatamiz:
let baseClient = URLSessionHTTPClient()
let clientWithCache = CachingHTTPClientDecorator(decoratee: baseClient)
let fullClient = LoggingHTTPClientDecorator(decoratee: clientWithCache)
// fullClient chaqirilganda avval Log yoziladi, keyin kesh tekshiriladi, agar yo'q bo'lsa tarmoqqa chiqadi!
3. Facade Pattern: Murakkab Tizimlarni 1 Qatorda Boshqarish
Facade (Bosh Oyna) — bu o'zida o'nlab murakkab quyi tizimlarni (Subsystems) birlashtirib, tashqi dunyoga bitta soddalashtirilgan interfeys taqdim etuvchi pattern.
graph TD
Client["ViewController / UI Qatlami"] --> Facade["AppStartupFacade.bootstrap()"]
subgraph Subsystems["Murakkab Ichki Quyi Tizimlar"]
direction TB
S1["DatabaseMigrationService.run()"]
S2["KeychainService.checkBiometrics()"]
S3["NotificationHelper.requestPermissions()"]
S4["AnalyticsService.setupTracker()"]
S5["RemoteConfig.syncFlags()"]
end
Facade --> S1
Facade --> S2
Facade --> S3
Facade --> S4
Facade --> S5
style Facade fill:#131720,stroke:#bef264,stroke-width:2px
style Subsystems fill:#181d26,stroke:#7da2ce,stroke-width:1px
Real Swift 6 Kodi:
public final class AppStartupFacade {
private let dbMigration = DatabaseMigrationService()
private let keychain = KeychainManager()
private let notifications = NotificationManager()
private let analytics = AnalyticsManager()
public init() {}
public func bootstrap() async {
print("Ilova ishga tushirilmoqda...")
await dbMigration.migrateIfNeeded()
await keychain.validateAccess()
await notifications.registerForPushNotifications()
analytics.configure()
print("Barcha 4 ta tizim tayyor!")
}
}
Endi AppDelegate yoki SwiftUI App faylida 50 qator kod yozish o'rniga faqat:
@main
struct MyApp: App {
private let startupFacade = AppStartupFacade()
var body: some Scene {
WindowGroup {
ContentView()
.task {
await startupFacade.bootstrap()
}
}
}
}
4. Interaktiv Savol-Javob (Self-Check Quiz)
<details> <summary><b>1-Savol: SwiftUI'dagi '.padding().background(.red).cornerRadius(10)' zanjiri qaysi design pattern asosida ishlaydi?</b> (Yechimni ko'rish uchun bosing)</summary>
Tahlil va Yechim: Bu 100% Decorator Patternidir! SwiftUI'dagi har bir ViewModifier o'zidan oldingi View'ni o'zining ichiga o'rab (wrap qilib), unga yangi ko'rinish yoki xatti-harakat qo'shadi va yangi bezatilgan View qaytaradi. </details>
<details> <summary><b>2-Savol: Facade patterni quyi tizimlarga to'g'ridan-to'g'ri murojaat qilishni taqiqlaydimi?</b> (Yechimni ko'rish uchun bosing)</summary>
Tahlil va Yechim: Yo'q, taqiqlamaydi. Facade faqat ko'p uchraydigan 90% holatlar uchun sodda yo'l beradi. Agar dasturchiga juda kam uchraydigan nozik sozlama kerak bo'lsa, u Facade'ni chetlab o'tib, to'g'ridan-to'g'ri ichki quyi tizim bilan ishlash huquqiga ega bo'lib qoladi. </details>
5. Amaliy Topshiriq (Senior Challenge)
UserLogoutFacade sinfini yarating:
- Foydalanuvchi "Chiqish" (Logout) tugmasini bosganda u ketma-ket:
KeychainManager.deleteTokens()SQLiteDatabase.clearUserTables()ImageCache.clearMemory()PushNotificationManager.unregisterDeviceToken()bajarilishini nazorat qilsin va muvaffaqiyatli yakunlangach UI ga xabar bersin!