Azimjon Abdurasulov

03. Factory Method va Abstract Factory: Moslashuvchan UI va Gateway

🏛️ iOS Design Patterns — Mukammal Enterprise & Architecture Qo‘llanma

03. Creational: Factory Method va Abstract Factory

"Yangi mahsulot turi (masalan, yangi to'lov tizimi yoki yangi UI kartochkasi) qo'shilganda, agar siz mavjud 10 ta fayldagi switch-case kodlarini ochib o'zgartirishga majbur bo'layotgan bo'lsangiz — siz Open/Closed tamoyilini buzdingiz. Factory aynan shu muammoni ildizi bilan yo'q qiladi."


1. Factory Method vs Abstract Factory: Asosiy Farq Nima?

Ko'p dasturchilar bu ikki patternni aralashtirib yuborishadi. Oddiy qoida:

  • Factory Method: Faqat bitta mahsulotni yaratishga mas'ul (masalan: createPaymentGateway() -> PaymentGateway).
  • Abstract Factory: Bir-biriga mantiqan bog'liq bo'lgan butun mahsulotlar oilasini yaratishga mas'ul (masalan: createButton(), createAlert(), createTextField()).
graph TD
    subgraph FM["1. Factory Method (1 ta mahsulot)"]
        direction TB
        FMethod["PaymentFactory.create(type)"] --> Gateway["Bitta Gateway qaytadi\n(Payme, Click yoki ApplePay)"]
    end

    subgraph AF["2. Abstract Factory (Mahsulotlar Oilasi)"]
        direction TB
        AFactory["UIThemeFactory (Interfeys)"]
        AFactory --> DarkF["DarkThemeFactory"]
        AFactory --> LightF["LightThemeFactory"]
        
        DarkF --> DarkBtn["Dark Button"]
        DarkF --> DarkAlert["Dark Alert"]
        DarkF --> DarkField["Dark TextField"]

        LightF --> LightBtn["Light Button"]
        LightF --> LightAlert["Light Alert"]
        LightF --> LightField["Light TextField"]
    end

    style FM fill:#131720,stroke:#7da2ce,stroke-width:1px
    style AF fill:#131720,stroke:#bef264,stroke-width:2px

2. Factory Method: Real FinTech To'lov Tizimi Misoli

Tasavvur qiling, ilovangizda Payme, Click va Apple Pay orqali to'lov mavjud.

Nega oddiy switch-case xato?

// ❌ OCP ni buzuvchi yondashuv:
func pay(type: String, amount: Double) {
    switch type {
    case "payme": PaymeSDK.charge(...)
    case "click": ClickSDK.charge(...)
    // Yangi 'uzum' qo'shilsa, bu kodni ochib o'zgartirish shart!
    }
}

To'g'ri Factory Method Yechimi:

// 1. Umumiy Mahsulot Protokoli
public protocol PaymentGateway: Sendable {
    func processPayment(amount: Double) async throws -> String
}

// 2. Aniq Mahsulotlar (Concrete Products)
public final class PaymeGateway: PaymentGateway {
    public func processPayment(amount: Double) async throws -> String {
        return "Payme orqali \(amount) so'm to'landi. Tranzaksiya: #PAY_102"
    }
}

public final class ClickGateway: PaymentGateway {
    public func processPayment(amount: Double) async throws -> String {
        return "Click orqali \(amount) so'm to'landi. Tranzaksiya: #CLK_904"
    }
}

public final class ApplePayGateway: PaymentGateway {
    public func processPayment(amount: Double) async throws -> String {
        return "Apple Pay orqali \(amount) so'm to'landi. Tranzaksiya: #APL_551"
    }
}

// 3. Factory Method
public enum PaymentMethodType {
    case payme
    case click
    case applePay
}

public struct PaymentGatewayFactory {
    public static func makeGateway(for type: PaymentMethodType) -> any PaymentGateway {
        switch type {
        case .payme:
            return PaymeGateway()
        case .click:
            return ClickGateway()
        case .applePay:
            return ApplePayGateway()
        }
    }
}

Endi ViewModel faqat PaymentGateway protokoli bilan ishlaydi. U Payme yoki Click qanday ishlashini bilishga majbur emas!


3. Abstract Factory: Multi-Brand & Multi-Theme UI Dvigateli

Bir xil ilovani 2 xil brend (masalan, Oq brend va Qora VIP brend) yoki iOS va macOS uchun chiqarmoqchisiz:

classDiagram
    class UIComponentFactory {
        <<interface>>
        +createButton() View
        +createCard() View
        +createBadge() View
    }

    class StandardThemeFactory {
        +createButton() StandardButton
        +createCard() StandardCard
        +createBadge() StandardBadge
    }

    class VIPGoldThemeFactory {
        +createButton() GoldButton
        +createCard() GoldCard
        +createBadge() GoldBadge
    }

    UIComponentFactory <|.. StandardThemeFactory
    UIComponentFactory <|.. VIPGoldThemeFactory

Swift 6 Implementatsiyasi:

import SwiftUI

// 1. Mahsulotlar protokollari
public protocol ThemedButton: View {}
public protocol ThemedCard: View {}

// 2. Abstrakt Fabrika Protokoli (Butun oilani birlashtiradi)
public protocol AppThemeFactory {
    associatedtype ButtonView: ThemedButton
    associatedtype CardView: ThemedCard
    
    func makeButton(title: String, action: @escaping () -> Void) -> ButtonView
    func makeCard(title: String, subtitle: String) -> CardView
}

// 3. Aniq Fabrika #1: Standart Mavzu
public struct StandardThemeFactory: AppThemeFactory {
    public func makeButton(title: String, action: @escaping () -> Void) -> some ThemedButton {
        StandardButtonImpl(title: title, action: action)
    }
    
    public func makeCard(title: String, subtitle: String) -> some ThemedCard {
        StandardCardImpl(title: title, subtitle: subtitle)
    }
}

// 4. Aniq Fabrika #2: VIP Oltin Mavzu
public struct VIPGoldThemeFactory: AppThemeFactory {
    public func makeButton(title: String, action: @escaping () -> Void) -> some ThemedButton {
        GoldButtonImpl(title: title, action: action)
    }
    
    public func makeCard(title: String, subtitle: String) -> some ThemedCard {
        GoldCardImpl(title: title, subtitle: subtitle)
    }
}

// Komponentlarning aniq SwiftUI ko'rinishlari
private struct StandardButtonImpl: ThemedButton {
    let title: String
    let action: () -> Void
    var body: some View {
        Button(title, action: action)
            .buttonStyle(.borderedProminent)
            .tint(.blue)
    }
}

private struct GoldButtonImpl: ThemedButton {
    let title: String
    let action: () -> Void
    var body: some View {
        Button(title, action: action)
            .buttonStyle(.borderedProminent)
            .tint(.yellow)
            .foregroundColor(.black)
    }
}

private struct StandardCardImpl: ThemedCard {
    let title: String
    let subtitle: String
    var body: some View {
        VStack { Text(title); Text(subtitle) }.padding().background(Color.gray.opacity(0.1))
    }
}

private struct GoldCardImpl: ThemedCard {
    let title: String
    let subtitle: String
    var body: some View {
        VStack { Text(title); Text(subtitle) }.padding().background(Color.yellow.opacity(0.2))
    }
}

4. Qachon Qaysi Biri Ishlatiladi?

MezonFactory MethodAbstract Factory
Yaratiladigan Obyektlar Soni1 ta alohida obyektBir-biriga bog'liq bir nechta obyektlar oilasi
Murakkablik DarajasiPast va tushunarliO'rtacha/Yuqori (ko'plab protokollar talab etiladi)
Qo'llash JoyiParserlar, To'lov tizimlari, Network ClientlarMulti-Brand tizimlar, Dizayn Tizimlari, Platformlararo UI

5. Interaktiv Savol-Javob (Self-Check Quiz)

<details> <summary><b>1-Savol: Nega Factory patterni Open/Closed (SOLID O) tamoyilini eng zo'r himoya qiluvchi pattern hisoblanadi?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim: Chunki ilovaning biznes logikasi (masalan, Savat yoki To'lov ViewModel'i) aniq sinflar (PaymeGateway, ClickGateway) ga emas, umumiy PaymentGateway protokoliga bog'langan bo'ladi. Yangi to'lov turi (masalan, UzumPay) qo'shilganda mavjud ViewModel'larning birorta satr kodiga tegilmaydi! Faqat yangi UzumPayGateway sinfi yoziladi va fabrikaga qo'shiladi. Kod o'zgarishlarga yopiq, kengayishga ochiq bo'ladi. </details>

<details> <summary><b>2-Savol: Swift'da Dependency Injection Container va Factory o'rtasida qanday farq bor?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim: Factory odatda berilgan parametrlar (type, config) asosida har safar yangi instancelar yaratib berishga xizmat qiladi. DI Container esa barcha ilova xizmatlarining (Singletons, Transient, Scoped xizmatlar) hayot siklini boshqaruvchi markazlashgan registrator hisoblanadi. Ko'p hollarda DI Container o'z ichida Factory patternidan foydalanadi. </details>


6. Amaliy Topshiriq (Senior Challenge)

Ilovangiz uchun DocumentExporterFactory yarating:

  1. PDFExporter, CSVExporter va JSONExporter sinflari bitta DocumentExporting protokolini tasdiqlasin.
  2. makeExporter(for format: ExportFormat) orqali kerakli formatga o'tkazuvchi Factory Method yozing.
  3. Foydalanuvchi ma'lumotlarini 3 xil formatda eksport qilishni simulyatsiya qilib ko'ring!

Markdown manba

iOS Lessons