Azimjon Abdurasulov

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 LoggedInEncryptedCachingNetworkClient degan 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!

Markdown manba

iOS Lessons