16. Senior Architecture Imtihoni va Capstone Challenge
🏛️ iOS Design Patterns — Mukammal Enterprise & Architecture Qo‘llanma
Senior & Staff iOS Architecture Imtihoni va Capstone Challenge
Ushbu yakuniy modulda siz iOS Design Patterns bo‘yicha to‘plagan barcha nazariy va amaliy bilimlaringizni sinovdan o‘tkazasiz. Bo‘lim ikki qismdan iborat:
- Senior / Lead darajasidagi 15 ta chuqur texnik intervyu savollari (Real-world keyslar, trade-offlar va pitfalls).
- Capstone Architecture Challenge: Bir nechta naqshlarni (Coordinator + Factory + Strategy + Repository + Observer + DI) uyg‘unlashtirgan to‘liq ishlaydigan FinTech Checkout & Payment System loyihasi.
1-Qism: Senior / Staff Intervyu Savollari va Chuqur Tahlil
O‘zingizni sinab ko‘ring! Savolga avval o‘zingiz javob berishga harakat qiling, so‘ng tahlilni ochib o‘qing.
<details> <summary><b>1. Singleton'ni Swift 6 da thread-safe qilishning eng zamonaviy va xavfsiz yo'li qaysi? Nega global mutable static let xavfli?</b></summary>
Javob: Swift'da static let shared = MyClass() o‘z-o‘zidan thread-safe initialization (dispatch_once asosida) beradi. Ammo agar MyClass ichida mutable property'lar (var) bo‘lsa, ko‘p oqimli (multithreaded) muhitda Data Race kelib chiqadi va Swift 6 Strict Concurrency buni compile xatosi deb hisoblaydi.
Eng toza va zamonaviy yechim — Singleton'ni actor sifatida e'lon qilish yoki uning ichki holatini @MainActor ga bog‘lash:
public actor SecureSessionManager {
public static let shared = SecureSessionManager()
private var token: String?
private init() {}
public func updateToken(_ newToken: String) {
self.token = newToken
}
}
Bu holatda Swift Concurrency kompilyatori barcha o‘qish va yozishlarni actor isolation orqali himoya qiladi va Data Race imkonsiz bo‘ladi. </details>
<details> <summary><b>2. Coordinator pattern'da Child Coordinator'lar xotirada qolib ketishining (Memory Leak) asosiy sababi nima va u qanday yechiladi?</b></summary>
Javob: Asosiy sabab — Parent Coordinator o‘zining childCoordinators massivida Child ob’yektni saqlaydi, ammo Child Coordinator o‘z ishini tugatib, ekran yopilganda (masalan, UINavigationController'ning orqaga "Back" tugmasi bosilganda yoki pop bo‘lganda) parent massividan o‘chirilmaydi.
Yechimi:
- Har bir Coordinator'ga
didFinish(coordinator:)delegate yoki closure mexanizmini kiritish. UINavigationControllerDelegateni tinglab, ekrandanpopbo‘lganUIViewControllerqaysi koordinatordan chiqqanini aniqlash va mos child'ni massivdan olib tashlash:
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
guard let fromVC = navigationController.transitionCoordinator?.viewController(forKey: .from),
!navigationController.viewControllers.contains(fromVC) else { return }
// fromVC ga tegishli child coordinatordan xalos bo'lish
}
</details>
<details> <summary><b>3. Adapter va Facade o'rtasidagi asosiy konseptual farq nimada?</b></summary>
Javob:
- Adapter — bu interfeysni o‘zgartiruvchi naqsh. Uning maqsadi mavjud bo‘lgan bitta interfeysni ikkinchi kutilayotgan interfeysga moslashtirish (masalan, Legacy completion-handler SDK ni zamonaviy
async/awaitprotocoliga o‘rash). U yangi xatti-harakat qo‘shmaydi, faqat "tarjima" qiladi. - Facade — bu murakkablikni yashiruvchi naqsh. U bitta emas, balki bir nechta mustaqil quyi tizimlarni (masalan, Audio, Bluetooth, Database, Network) bitta qulay, soddalashtirilgan interfeys orqasida birlashtiradi (
AppStartupFacade.start()).
</details>
<details> <summary><b>4. Repository Pattern'da "Single Source of Truth" (SSOT) nimani anglatadi va Stale-While-Revalidate qanday ishlaydi?</b></summary>
Javob: SSOT (Yagona Haqiqat Manbai) — UI qatlami hech qachon "Network'dan ma'lumot oldimmi yoki Local DB'danmi?" deb o‘ylamasligi kerakligini bildiradi. UI faqat bitta manbaga — masalan, Local Database (SQLite / Realm) ga obuna bo‘ladi.
Stale-While-Revalidate oqimida:
- Repository birinchi navbatda Local Cache'dagi eski (stale) ma’lumotni darhol qaytaradi (UI lahzada ochiladi).
- Orqa fonda tarmoqdan yangi ma’lumot so‘raladi.
- Yangi ma’lumot kelgach, u Local DB ga saqlanadi.
- Local DB dagi o‘zgarish reaktiv tarzda (Observer / AsyncStream orqali) UI ga uzatiladi.
</details>
<details> <summary><b>5. Delegation pattern'da protocol nega AnyObject dan meros olishi shart va weak kalit so'zi qanday rol o'ynaydi?</b></summary>
Javob: Swift'da weak faqat Reference Type (Class) larda ishlatilishi mumkin, Value Type (Struct) larda reference count tushunchasi yo‘q. Agar protocol : AnyObject deb e'lon qilinmasa, kompilyator unga Struct ham kelishi mumkin deb hisoblaydi va weak var delegate: MyDelegate? deb yozishga ruxsat bermaydi. weak yozilmasa, Presenter -> View va View -> Presenter (delegate) o‘rtasida Strong Reference Cycle (Retain Cycle) yuzaga keladi va ekran yopilganda ham xotiradan o‘chmaydi (ARC count hech qachon 0 ga tushmaydi). </details>
<details> <summary><b>6. Strategy va State pattern'larining class diagrammasi deyarli bir xil. Ularni qanday farqlash mumkin?</b></summary>
Javob: Konseptual farq ularning maqsadi va holat almashishida:
- Strategy — biror aniq vazifani (algoritm) turli xil usullarda bajarish (masalan, to‘lovni PayPal yoki Card orqali qilish). Mijoz odatda strategiyani o‘zi tanlaydi va u butun operatsiya davomida kamdan-kam o‘zgaradi. Strategiyalar bir-biridan mustaqil.
- State — ob’yektning ichki holati o‘zgarganda uning xatti-harakatini to‘liq o‘zgarishi (Finite State Machine). Muhimi: holatlar o‘zlari navbatdagi holatga o‘tishni bilishi va boshqarishi mumkin (
Connecting -> Connected -> Disconnected).
</details>
<details> <summary><b>7. Service Locator nima sababdan ko'plab arxitektorlar tomonidan Anti-Pattern deb baholanadi?</b></summary>
Javob:
- Yashirin qaramliklar: Ob’yektning
initida hech qanday parametr ko‘rinmaydi, lekin ichidaServiceLocator.shared.get()chaqiriladi. - Kompilyatsiya xavfsizligi yo'qligi: Agar zarur servis unutilgan bo‘lsa, kod compile bo‘ladi, ammo dastur ish paytida (runtime) kutilmaganda crash bo‘ladi.
- Unit test qiyinligi: Test yozish uchun global Service Locator holatini tozalash va har bir test uchun qayta to‘ldirish zarur bo‘ladi, bu esa testlarning parallel ishlashini buzadi.
</details>
<details> <summary><b>8. Decorator pattern Swift'da Subclassing'dan qanday ustunlik beradi?</b></summary>
Javob: Subclassing (merosxo‘rlik) statik bo‘lib, compile-time'da aniqlanadi. Agar sizda 4 ta qo‘shimcha funksiya (Kesh, Log, Shifrlash, Siqish) bo‘lsa, subclassing orqali har bir kombinatsiya uchun yangi sinf yaratish talab etiladi (Class Explosion — $2^4 = 16$ ta sinf!). Decorator esa kompozitsiya asosida ishlaydi: siz funksiyalarni dinamik ravishda bir-birining ichiga joylab (o‘rab), istalgan ketma-ketlikda yig‘ishingiz mumkin: Logging(Caching(Encryption(BaseService()))). </details>
<details> <summary><b>9. Observer pattern'da Memory Leak kelib chiqmasligi uchun nimalarga e'tibor berish zarur?</b></summary>
Javob: Agar Observer'lar kolleksiyada strong reference bilan saqlansa, ular hech qachon deallocated bo‘lmaydi. Buni hal qilish uchun:
WeakBox(weak reference o‘rovchi struktura) dan foydalanish.- NotificationCenter da
removeObserver()chaqirish yoki Combine'daAnyCancellableni xotiradan tozalash. - Swift 6 da esa zamonaviy
AsyncStreamlardan foydalanib, task bekor bo‘lganda (onTermination) avtomatik tozalanishini ta’minlash.
</details>
<details> <summary><b>10. Telescoping Constructor muammosi nima va u qaysi naqsh bilan yechiladi?</b></summary>
Javob: Telescoping Constructor — ob’yektda juda ko‘p ixtiyoriy (optional) parametrlar bo‘lganda, parametrlar soni oshib ketib, init(a: b: c: d: e: f: g:) ko‘rinishidagi o‘qib bo‘lmas konstruktorlar paydo bo‘lishi. Bu muammo Builder Pattern yordamida yechiladi. Fluent API orqali faqat kerakli parametrlar zanjir ko‘rinishida o‘rnatiladi (RequestBuilder().setURL(...).setMethod(...).build()). </details>
2-Qism: Capstone Architecture Challenge — FinTech Core Engine
Quyidagi topshiriq barcha o‘rganilgan naqshlarni bitta tizimda birlashtiradi.
Loyiha Arxitekturasi (Mermaid)
graph TD
subgraph UI & Navigation
Coord[CheckoutCoordinator] -->|Boshqaradi| VC[CheckoutViewController]
end
subgraph Dependency Injection
DI[DIContainer] -.->|Injects| Coord
DI -.->|Injects| Repo[PaymentRepository]
end
subgraph Business Logic & Data
VC -->|Foydalanadi| Repo
Repo -->|Single Source of Truth| Cache[(Local SQLite / Cache)]
Repo -->|Tarmoq orqali sinxronlash| API[RemotePaymentAPI]
end
subgraph Factory & Strategy
VC -->|Tanlaydi| StratFactory[PaymentStrategyFactory]
StratFactory -->|Yaratadi| Strat1[ApplePayStrategy]
StratFactory -->|Yaratadi| Strat2[CreditCardStrategy]
StratFactory -->|Yaratadi| Strat3[CryptoStrategy]
VC -->|Ijro etadi| Exec[PaymentExecutor]
Exec -->|Strategy orqali to'laydi| Strat1
end
subgraph Reactive Observer
Rates[ExchangeRateFeed] -->|AsyncStream Observer| VC
end
Ishlaydigan To‘liq Swift 6 Kod Namunasi
Ushbu kodni to‘g‘ridan-to‘g‘ri Xcode Playgrounds yoki Swift loyihangizda ishga tushirishingiz mumkin:
import Foundation
// MARK: - 1. Domain Models
public struct Money: Sendable, Equatable {
public let amount: Decimal
public let currency: String
}
public enum PaymentResult: Sendable {
case success(transactionID: String)
case failure(reason: String)
}
// MARK: - 2. Strategy Pattern: To'lov Usullari
public protocol PaymentStrategy: Sendable {
var title: String { get }
func processPayment(amount: Money) async throws -> PaymentResult
}
public struct ApplePayStrategy: PaymentStrategy {
public let title = "Apple Pay"
public func processPayment(amount: Money) async throws -> PaymentResult {
// Apple Pay tokenizatsiya va biometrik tekshiruv...
return .success(transactionID: "APL-\(UUID().uuidString.prefix(8))")
}
}
public struct CreditCardStrategy: PaymentStrategy {
public let title = "Credit Card (Stripe)"
private let cardNumber: String
public init(cardNumber: String) {
self.cardNumber = cardNumber
}
public func processPayment(amount: Money) async throws -> PaymentResult {
return .success(transactionID: "CC-\(UUID().uuidString.prefix(8))")
}
}
// MARK: - 3. Factory Pattern: Strategiya Yaratuvchi
public enum PaymentMethodType: Sendable {
case applePay
case creditCard(number: String)
}
public struct PaymentStrategyFactory {
public static func makeStrategy(for type: PaymentMethodType) -> any PaymentStrategy {
switch type {
case .applePay:
return ApplePayStrategy()
case .creditCard(let number):
return CreditCardStrategy(cardNumber: number)
}
}
}
// MARK: - 4. Observer Pattern: Valyuta Kurslari Oqimi (AsyncStream)
public final class LiveExchangeRateFeed: @unchecked Sendable {
public static let shared = LiveExchangeRateFeed()
private init() {}
public func rateStream() -> AsyncStream<Decimal> {
AsyncStream { continuation in
let timer = DispatchSource.makeTimerSource()
timer.schedule(deadline: .now(), repeating: .seconds(2))
var currentRate: Decimal = 12750.0
timer.setEventHandler {
currentRate += Decimal(Double.random(in: -10...10))
continuation.yield(currentRate)
}
continuation.onTermination = { _ in
timer.cancel()
}
timer.resume()
}
}
}
// MARK: - 5. Repository Pattern: Offline-First To'lov Tarixi
public protocol PaymentHistoryRepository: Sendable {
func getHistory() async -> [String]
func record(transactionID: String) async
}
public actor LocalPaymentRepository: PaymentHistoryRepository {
private var transactions: [String] = []
public init() {}
public func getHistory() -> [String] {
return transactions
}
public func record(transactionID: String) {
transactions.append(transactionID)
}
}
// MARK: - 6. Coordinator Pattern: Navigatsiya
@MainActor
public protocol Coordinator: AnyObject {
func start()
}
@MainActor
public final class CheckoutCoordinator: Coordinator {
private let repository: any PaymentHistoryRepository
public init(repository: any PaymentHistoryRepository) {
self.repository = repository
}
public func start() {
print("🧭 CheckoutCoordinator: Checkout oqimi boshlandi.")
}
public func completeCheckout(transactionID: String) async {
await repository.record(transactionID: transactionID)
print("🎉 Checkout muvaffaqiyatli yakunlandi! ID: \(transactionID)")
}
}
// MARK: - 7. Asosiy Orkestratsiya (Amalda Ishga Tushirish)
@main
struct CapstoneRunner {
static func main() async {
print("🚀 FinTech Engine ishga tushmoqda...")
let repository = LocalPaymentRepository()
let coordinator = await CheckoutCoordinator(repository: repository)
await coordinator.start()
// 1. Factory orqali strategiya tanlash
let method = PaymentMethodType.applePay
let strategy = PaymentStrategyFactory.makeStrategy(for: method)
print("💳 Tanlangan to'lov usuli: \(strategy.title)")
// 2. To'lovni amalga oshirish
let cartTotal = Money(amount: 150.00, currency: "USD")
do {
let result = try await strategy.processPayment(amount: cartTotal)
switch result {
case .success(let txID):
await coordinator.completeCheckout(transactionID: txID)
case .failure(let reason):
print("❌ Xatolik: \(reason)")
}
} catch {
print("❌ Tizim xatosi: \(error)")
}
// 3. Repository holatini tekshirish
let history = await repository.getHistory()
print("📜 Saqlangan tranzaksiyalar: \(history)")
}
}
🏆 Tabriklaymiz!
Siz iOS Design Patterns — Mukammal Enterprise & Architecture Qo‘llanma modulini to‘liq yakunladingiz!
Siz endi:
- Har bir naqsh qachon kerak va qachon anti-patternga aylanishini bilasiz;
- Xotira boshqaruvi (ARC, Retain Cycles) va Swift 6 Concurrency talablariga mos kod yoza olasiz;
- Massive View Controller'larni yo‘q qilib, modulli, testlanuvchi va kengayuvchan korporativ tizimlar qura olasiz.