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-elsebilan 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:
- Foydalanuvchi metroda (internet yo'q) xabar yozadi yoki do'kondan narsa xarid qiladi.
- Ilova darhol xatolik bermaydi, balki harakatni
SendMessageCommand(message)sifatida diskka yozib qo'yadi. - 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 ravishdaIdleholatiga 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!