Azimjon Abdurasulov

09. Strategy va Command: To'lov Tizimi va Undo/Redo Engine

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

09. Behavioral: Strategy va Command Pattern

"Agar siz har xil to'lov usuli yoki saralash algoritmi uchun butun klass ichini 20 ta if-else bilan to'ldirib tashlasangiz — bu Strategy patterniga muhtojlik belgisidir. Agar sizga 'Bekor qilish' (Undo) yoki so'rovlarni navbatga terish kerak bo'lsa — Command sizning najotingizdir."


1. Strategy Pattern: Algoritmlarni Erkin Almashtirish

Strategy (Strategiya) — bu bir xil vazifani bajaruvchi algoritmlar oilasini yaratish, ularning har birini alohida sinfga o'rash va ularni dastur ishlashi davomida (Runtime) bir-birining o'rniga erkin almashtirish imkonini beruvchi pattern.

graph TD
    Client["CheckoutController / Kontekst"] --> Strategy["Interfeys: PaymentStrategy"]
    
    Strategy -.-> S1["PaymeStrategy\n(O'zbekiston ichki kartalari)"]
    Strategy -.-> S2["ClickStrategy\n(USSD va Karta)"]
    Strategy -.-> S3["ApplePayStrategy\n(Biometrika orqali xalqaro)"]
    Strategy -.-> S4["InstallmentStrategy\n(12 oyga bo'lib to'lash)"]

    style Client fill:#131720,stroke:#bef264,stroke-width:2px
    style Strategy fill:#181d26,stroke:#7da2ce,stroke-width:2px

Swift 6 Real Kodi: To'lov Strategiyalari

import Foundation

// 1. Strategiya Protokoli
public protocol PaymentStrategy: Sendable {
    func pay(amount: Double) async throws -> String
}

// 2. Aniq Strategiyalar
public struct PaymeStrategy: PaymentStrategy {
    public func pay(amount: Double) async throws -> String {
        return "Payme orqali \(amount) so'm to'landi"
    }
}

public struct ClickStrategy: PaymentStrategy {
    public func pay(amount: Double) async throws -> String {
        return "Click orqali \(amount) so'm to'landi"
    }
}

public struct ApplePayStrategy: PaymentStrategy {
    public func pay(amount: Double) async throws -> String {
        return "Apple Pay orqali \(amount) so'm to'landi"
    }
}

// 3. Kontekst (Context)
@MainActor
public final class CheckoutContext {
    private var strategy: any PaymentStrategy
    
    public init(strategy: any PaymentStrategy) {
        self.strategy = strategy
    }
    
    public func setStrategy(_ newStrategy: any PaymentStrategy) {
        self.strategy = newStrategy
    }
    
    public func completeCheckout(amount: Double) async throws -> String {
        return try await strategy.pay(amount: amount)
    }
}

Foydalanuvchi ekranda to'lov usulini o'zgartirishi bilan: context.setStrategy(ApplePayStrategy()) chaqiriladi. Kodda birorta ham if-else yo'q!


2. Command Pattern: "Bekor Qilish" (Undo/Redo) va So'rovlar Navbati

Command (Buyruq) — bu so'rov yoki harakatni (Action) o'z parametrlari bilan birga alohida mustaqil obyekt ko'rinishida saqlash imkonini beruvchi pattern.

sequenceDiagram
    autonumber
    participant User as Foydalanuvchi
    participant Manager as CommandHistoryManager
    participant Cmd as TransferMoneyCommand

    User->>Manager: "100,000 so'm o'tkazish" (execute)
    Manager->>Cmd: execute()
    Cmd-->>Manager: Muvaffaqiyatli! Tarixga yozildi.
    
    User->>Manager: "Adashdim, bekor qil!" (Undo tugmasi)
    Manager->>Cmd: undo()
    Cmd-->>User: Pul qaytarildi va balans tiklandi!

3. Real Swift 6 Kodi: Matn Muharriri Uchun Undo/Redo

import Foundation

// 1. Buyruq Protokoli
public protocol Command {
    func execute()
    func undo()
}

// 2. Qabul Qiluvchi (Receiver - haqiqiy ishni bajaruvchi)
public final class CanvasDocument {
    public var text: String = ""
    
    public func appendText(_ newText: String) {
        text += newText
    }
    
    public func removeLast(count: Int) {
        text = String(text.dropLast(count))
    }
}

// 3. Aniq Buyruq
public final class AppendTextCommand: Command {
    private let document: CanvasDocument
    private let textToAppend: String
    
    public init(document: CanvasDocument, textToAppend: String) {
        self.document = document
        self.textToAppend = textToAppend
    }
    
    public func execute() {
        document.appendText(textToAppend)
    }
    
    public func undo() {
        document.removeLast(count: textToAppend.count)
    }
}

// 4. Tarix Boshqaruvchisi (Invoker)
public final class CommandInvoker {
    private var undoStack: [any Command] = []
    private var redoStack: [any Command] = []
    
    public func executeCommand(_ command: any Command) {
        command.execute()
        undoStack.append(command)
        redoStack.removeAll() // Yangi buyruq kelganda redo tozalanadi
    }
    
    public func undo() {
        guard let command = undoStack.popLast() else { return }
        command.undo()
        redoStack.append(command)
    }
    
    public func redo() {
        guard let command = redoStack.popLast() else { return }
        command.execute()
        undoStack.append(command)
    }
}

4. Offline Request Queue (Internet Yo'qligida Saqlab Qolish)

Mobil ilovalarda Command patternining eng katta qo'llanilish sohasi — bu Offline Transaction Queue dir:

  1. Foydalanuvchi metroda (internet yo'q) xabar yozadi yoki do'kondan narsa xarid qiladi.
  2. Ilova darhol xatolik bermaydi, balki harakatni SendMessageCommand(message) sifatida diskka yozib qo'yadi.
  3. Internet paydo bo'lishi bilan navbatdagi barcha buyruqlar avtomatik execute() qilinadi!

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

<details> <summary><b>1-Savol: Strategy va State patternlari tuzilishi jihatidan deyarli bir xil. Ularning tub farqi nimada?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim:

  • Strategy patternida chaqiruvchi (Client) qaysi strategiyani ishlatishni odatda o'zi tanlaydi (masalan, foydalanuvchi Payme tugmasini bosdi). Strategiyalar bir-biridan mustaqil bo'ladi.
  • State patternida esa holatlar (States) ichki voqealar natijasida o'z-o'zidan biridan ikkinchisiga o'tib boradi (masalan, Audio Player: Playing -> qo'shiq tugadi -> avtomatik ravishda Idle holatiga o'tadi).

</details>

<details> <summary><b>2-Savol: Command patternida xotira cheksiz to'lib ketmasligi uchun nima qilish kerak?</b> (Yechimni ko'rish uchun bosing)</summary>

Tahlil va Yechim: undoStack hajmiga qat'iy cheklov (masalan, maksimal 50 ta oxirgi buyruq) qo'yilishi lozim (Ring Buffer yoki cheklangan massiv). 50 tadan oshganda eng birinchi eski buyruq xotiradan o'chirib tashlanadi. </details>


6. Amaliy Topshiriq (Senior Challenge)

CartOperationCommand tizimini yarating:

  • Foydalanuvchi savatga mahsulot qo'shishi (AddProductCommand), mahsulotni o'chirishi (RemoveProductCommand) va narxini o'zgartirishi mumkin bo'lsin.
  • Har bir amalni "Bekor qilish" (Undo) va "Qaytarish" (Redo) qilish imkonini beruvchi boshqaruvchini test qiling!

Markdown manba

iOS Lessons