Inversion of Control — dastur oqimi va dependency yaratish nazoratini application classlaridan framework, container yoki tashqi orchestratorga topshiradigan arxitektura tamoyili. Qisqartmasi IoC.
An’anaviy kod boshqa componentlarni o‘zi yaratib chaqiradi. IoC’da esa component framework tomonidan yaratiladi, kerakli dependencylar beriladi va lifecycle tashqaridan boshqariladi.
Oddiy nazorat
Class dependency’ni o‘zi yaratishi mumkin:
class OrderService:
def __init__(self):
self.repository = PostgresOrderRepository()
Bu classni konkret database implementationiga bog‘laydi.
Testda boshqa repository berish qiyinlashadi.
Inversiya
IoC bilan dependency tashqaridan keladi:
class OrderService:
def __init__(self, repository):
self.repository = repository
Application composition root’da concrete repository tanlanadi.
OrderService faqat contractga bog‘lanadi.
Dependency Injection
Dependency Injection IoC’ni amalga oshirishning keng tarqalgan usuli.
Dependencylar:
- constructor;
- method;
- property;
- framework field
orqali beriladi.
Constructor injection required dependencylar uchun eng aniq usullardan biri.
IoC container
Container component registration va resolutionni boshqaradi.
U biladi:
- qaysi interface uchun qaysi implementation;
- object lifecycle;
- scope;
- factory;
- configuration;
- decorator;
- dependency graph.
Container application startupda graphni yaratadi.
Registration
Dependency mapping explicit ro‘yxatdan o‘tkaziladi.
Masalan:
OrderRepository → PostgresOrderRepository
PaymentGateway → PaymeAdapter
Clock → SystemClock
Test environment boshqa implementationlarni register qiladi.
Resolution
Container class constructorini tekshirib kerakli dependencylarni topadi.
Keyin ularning dependencylarini ham yaratadi.
Bu recursive object graph resolution.
Cycle mavjud bo‘lsa container xato beradi yoki lazy reference talab qiladi.
Composition root
Barcha concrete dependencylar applicationning bitta yuqori qatlamida birlashtiriladi.
Bu composition root.
Domain va service classlar container API’sini chaqirmaydi.
Aks holda Service Locator anti-patterniga yaqin yashirin dependency paydo bo‘ladi.
Framework callback
IoC faqat dependency injection emas.
Web framework:
- requestni qabul qiladi;
- route topadi;
- controller methodini chaqiradi;
- response’ni serialize qiladi.
Application framework loopini o‘zi chaqirmaydi.
Framework application kodini kerakli vaqtda chaqiradi.
Event-driven IoC
Event loop callback yoki handlerlarni hodisa kelganda chaqiradi.
Application:
event bo‘lsa shu handlerni ishlat
deb ro‘yxatdan o‘tadi.
Qachon chaqirilishini event loop boshqaradi.
Template Method bilan aloqa
Base framework algoritm skeletini boshqarib, subclass hooklarini chaqirishi ham IoC ko‘rinishi.
Bu Hollywood Principle bilan ifodalanadi.
Application frameworkni boshqarish o‘rniga framework application extensionlarini chaqiradi.
Lifecycle
Container obyekt scope’ini boshqarishi mumkin:
- singleton;
- request;
- session;
- transient;
- custom scope.
Masalan, database session request boshida yaratiladi va oxirida yopiladi.
Noto‘g‘ri scope shared mutable state yoki connection leak yaratadi.
Singleton
Bitta application instance davomida bir obyekt.
Stateless service va configuration uchun qulay.
Mutable singleton thread safety va test isolation muammosini yaratishi mumkin.
Har narsani singleton qilish noto‘g‘ri.
Transient
Har resolutionda yangi instance yaratiladi.
Yengil, stateless objectlar uchun.
Agar uning ichida qimmat connection ochilsa resource sarfi oshadi.
Lifecycle dependency graph bilan mos bo‘lishi kerak.
Request scope
Har HTTP request uchun bitta instance.
Masalan:
Request tugaganda cleanup bajariladi.
Background taskda request scope mavjud bo‘lmasligi mumkin.
Testability
IoC classni fake dependency bilan test qilishni osonlashtiradi.
OrderService(FakeRepository, FakeClock)
Global singleton va hard-coded constructor chaqiriqlari kamayadi.
Test container ishlatish shart emas; class bevosita yaratilishi mumkin.
Loose coupling
High-level policy concrete low-level detailga emas, abstractionga bog‘lanadi.
Bu Dependency Inversion Principle bilan bog‘liq.
IoC bu bog‘lanishni runtime composition orqali amalga oshirishi mumkin.
Configuration
Environmentga qarab implementation tanlanadi.
Masalan:
Configuration class ichida if environment bilan tarqalib ketmaydi.
Kamchiliklari
Container object creationni yashirishi mumkin.
Debuggingda dependency qayerdan kelganini topish qiyinlashadi.
Reflection va automatic scanning startup xatolarini runtime’da ko‘rsatishi mumkin.
Juda ko‘p abstraction oddiy code’ni murakkablashtiradi.
Dependency graph validation
Container application startupida barcha registrationlarni tekshirishi mumkin.
Aniqlanadi:
- registration yo‘q;
- cycle;
- noto‘g‘ri scope;
- bir nechta noaniq implementation;
- constructor xatosi.
Bunday xatoni birinchi production requestda emas, startup yoki testda topish afzal.
Service Locator muammosi
Class global container’dan dependency olsa constructor sodda ko‘rinadi, ammo haqiqiy dependency yashirinadi.
Test global registryni sozlashga majbur bo‘ladi.
Service Locator IoC vositasi bo‘lishi mumkin, lekin domain class ichida keng ishlatilsa dependency transparency yo‘qoladi.
Disposal
Container yaratgan resource’larni lifecycle oxirida yopadi.
Masalan:
Transient obyekt ichida disposable resource yaratilsa owner kim ekani aniq bo‘lishi kerak.
Scope mismatch
Qisqa umrli request-scoped dependency uzoq umrli singleton ichiga inject qilinsa eski request state saqlanib qolishi mumkin.
Container bunday captive dependency’ni aniqlashi yoki factory orqali har chaqiriqda to‘g‘ri scope’dan olishni talab qilishi mumkin.
Conditional registration
Environment yoki feature bo‘yicha boshqa implementation tanlanishi mumkin.
Shartlar ko‘payib ketganda composition root murakkablashadi.
Registration business code ichiga tarqatilmaydi va startup testlarda barcha environment variantlari tekshiriladi.
Bog‘liq tushunchalar
Dependency Injection, IoC container, Composition root, Dependency Inversion Principle, Framework, Lifecycle, Constructor injection, Service Locator, Loose coupling