Adapter Pattern — bir class yoki tizim interface’ini client kutayotgan boshqa interface’ga moslashtiradigan structural design pattern. Adapter mos kelmaydigan komponentlarni ularning ichki kodini o‘zgartirmasdan birga ishlatishga yordam beradi.
Adapter elektr qurilmasi uchun ulagich adapteriga o‘xshaydi: manba va iste’molchi o‘zgarmaydi, ular orasida moslashtiruvchi qatlam paydo bo‘ladi.
Muammo
Application quyidagi interface’ni kutishi mumkin:
class PaymentGateway:
def pay(self, amount: int) -> str:
...
Tashqi provider esa boshqa method beradi:
provider.create_charge(summa, currency)
Clientni provider API’ga qattiq bog‘lash o‘rniga adapter yaratiladi.
Target
Target — client kutayotgan interface.
Client faqat shu contract bilan ishlaydi.
Masalan:
pay(amount)
refund(transaction_id)
status(transaction_id)
Target domain tiliga mos bo‘lishi kerak.
Adaptee
Adaptee — mavjud, ammo interface’i clientga mos kelmaydigan class yoki service.
Bu:
bo‘lishi mumkin.
Adaptee kodini o‘zgartirish imkonsiz yoki xavfli bo‘lishi mumkin.
Adapter
Adapter Target interface’ni amalga oshiradi va chaqiriqlarni Adaptee formatiga tarjima qiladi.
U:
- method nomini moslashtiradi;
- argumentlarni o‘zgartiradi;
- return qiymatni domain formatiga aylantiradi;
- exceptionni map qiladi;
- unit conversion bajaradi.
Adapter business qoidalarni ortiqcha yig‘masligi kerak.
Object adapter
Object adapter composition’dan foydalanadi.
class ProviderAdapter(PaymentGateway):
def __init__(self, provider):
self.provider = provider
Adapter ichida adaptee instance saqlanadi.
Bu ko‘p tillarda qulay va moslashuvchan.
Bir adaptee o‘rniga boshqasini inject qilish mumkin.
Class adapter
Class adapter inheritance orqali Target va Adaptee’ni bog‘laydi.
Bu multiple inheritance mavjud tillarda ishlashi mumkin.
Kamchiligi — konkret classga qattiq bog‘lanish va inheritance cheklovlari.
Composition asosidagi object adapter ko‘proq qo‘llanadi.
Two-way adapter
Ba’zi holatda ikki interface orasida ikki tomonlama moslashtirish kerak.
Adapter Target chaqiriqlarini Adaptee’ga, Adaptee formatini esa Targetga aylantiradi.
Bu murakkablikni oshiradi va barcha operationlar simmetrik bo‘lmasligi mumkin.
Data mapping
Adapter field nomlarini moslashtiradi.
Masalan:
first_name → givenName
created_at → createdAt
amount_uzs → amount
Mappingda:
- missing field;
- null;
- enum;
- date;
- timezone;
- precision
aniq boshqariladi.
Type conversion
Tashqi tizim decimal string qaytarishi, application esa integer tiyin ishlatishi mumkin.
Adapter conversionni markazlashtiradi.
Pul qiymatida floating-point conversiondan qochiladi.
Unit va rounding qoidalari hujjatlashtiriladi.
Error mapping
Provider exceptionlari domain errorlariga aylantiriladi.
Masalan:
ProviderTimeout → PaymentTemporaryError
InvalidCard → PaymentRejected
Unauthorized → ProviderConfigurationError
Client tashqi SDK exceptionlarini bilmaydi.
Retry faqat vaqtinchalik xatolarga qo‘llanadi.
Legacy integration
Eski class methodlari yangi arxitektura contractiga mos kelmasligi mumkin.
Adapter legacy kodni to‘liq qayta yozmasdan yangi service ichida ishlatadi.
Bu migrationni bosqichma-bosqich bajarishga yordam beradi.
Keyinchalik legacy component olib tashlansa adapter ham o‘chiriladi.
Tashqi API adapteri
Har tashqi provider uchun alohida adapter yaratilishi mumkin.
Masalan:
ClickAdapter
PaymeAdapter
MockPaymentAdapter
Application bir xil PaymentGateway interface’iga tayanadi.
Provider almashishi domain code’ni o‘zgartirmaydi.
Adapter va Facade
Adapter interface’ni boshqa interface’ga moslashtiradi.
Facade esa murakkab subsystem uchun soddalashtirilgan umumiy interface beradi.
Facade ichida bir nechta adapter bo‘lishi mumkin.
Adapter va Decorator
Adapter interface’ni o‘zgartiradi.
Decorator odatda shu interface’ni saqlab, yangi xatti-harakat qo‘shadi.
Masalan:
- adapter provider SDK’ni domain gatewayga aylantiradi;
- decorator gatewayga logging yoki retry qo‘shadi.
Adapter va Proxy
Proxy odatda shu interface’ni saqlaydi va accessni boshqaradi.
Adapter esa client va adaptee interface’lari mos kelmaganda tarjima qiladi.
Amalda bir class ikkala vazifani qisman bajarishi mumkin, ammo asosiy niyat farq qiladi.
Test
Adapter unit testlari quyidagilarni tekshiradi:
- argument mapping;
- response mapping;
- error mapping;
- null;
- timeout;
- currency va timezone;
- providerning yangi versiyasi.
Tashqi SDK integration testi ham kerak.
Contract test
Barcha adapterlar umumiy Target contractga mosligini bir xil test suite bilan tekshirish mumkin.
Masalan, har payment adapter:
- musbat amount qabul qiladi;
- transaction ID qaytaradi;
- vaqtinchalik xatoni bir xil domain errorga aylantiradi.
Bu providerlar orasidagi xatti-harakatni yaqinlashtiradi.
Afzalliklari
Adapter:
- legacy va yangi kodni bog‘laydi;
- tashqi dependency’ni yashiradi;
- domain interface’ni saqlaydi;
- migrationni bosqichlaydi;
- test double yaratishni osonlashtiradi.
Kamchiliklari
Ko‘p adapter qatlamlari debuggingni qiyinlashtirishi mumkin.
Tashqi API imkoniyatlari Target interface’da yo‘qolishi ehtimoli bor.
Universal interface barcha provider farqlarini yashira olmasligi mumkin.
Default qiymat va yo‘q field
Tashqi provider ayrim fieldni yubormasligi mumkin.
Adapter missing, null va bo‘sh string holatlarini bir xil deb qabul qilmasligi kerak.
Masalan, middle_name yo‘qligi bilan bo‘sh qiymatning business ma’nosi farq qilishi mumkin.
Mapping defaultlari domain qoidasi asosida qo‘llanadi.
Capability farqi
Barcha provider bir xil imkoniyatga ega emas.
Bitta payment provider partial refundni, boshqasi faqat full refundni qo‘llashi mumkin.
Target interface eng kichik umumiy denominatorga tushib qolmasligi uchun capability explicit ko‘rsatiladi yoki alohida interface’lar yaratiladi.
Version compatibility
Adapter tashqi SDK yoki API versioniga bog‘langan bo‘ladi.
Provider yangi required field qo‘shsa contract test xatoni erta ko‘rsatadi.
Version upgrade alohida adapterda sinovdan o‘tkazilib, keyin traffic bosqichma-bosqich yangi variantga o‘tkaziladi.
Bog‘liq tushunchalar
Structural pattern, Target, Adaptee, Object adapter, Interface, Facade, Decorator, Proxy, Legacy system, Integration layer