Polymorphism — bir xil interfeys yoki operatsiyaning turli type va obyektlarda o‘ziga mos xatti-harakat bilan ishlashi. Atama yunoncha “ko‘p shakl” ma’nosidan kelib chiqqan.
Dasturlashda polymorphism caller kodiga aniq implementationni bilmasdan umumiy contract orqali ishlash imkonini beradi.
Masalan, draw() amali doira, to‘rtburchak va matn obyektida turlicha bajarilishi mumkin.
Asosiy maqsad
Polymorphism quyidagilarni ta’minlaydi:
- umumiy interfeys;
- implementationni almashtirish;
- type bo‘yicha katta conditionalni kamaytirish;
- yangi variant qo‘shishni soddalashtirish;
- dependency inversion;
- testda fake implementation ishlatish.
U kodni avtomatik ravishda yaxshi qilmaydi. Contract noto‘g‘ri bo‘lsa, implementationlar o‘rtasidagi farq yashirin xatoga aylanadi.
Subtype polymorphism
Child class parent type sifatida ishlatiladi.
Shape shape = Circle()
shape.area()
Variable Shape type’da, haqiqiy object Circle.
Runtime Circle.area() methodini tanlaydi.
Bu inheritance va virtual method bilan bog‘liq.
Dynamic dispatch
Qaysi method chaqirilishi runtime type asosida aniqlansa dynamic dispatch deyiladi.
Object ichida yoki type metadata’da method table bo‘lishi mumkin.
Virtual call:
- object type aniqlanadi;
- method slot topiladi;
- tegishli implementationga o‘tiladi.
JIT ba’zan haqiqiy type barqaror bo‘lsa virtual callni devirtualize qiladi.
Static polymorphism
Qaysi implementation ishlashi compile vaqtida aniqlanishi ham mumkin.
Misollar:
- function overloading;
- operator overloading;
- generics specialization;
- template;
- trait constraint bilan monomorphization.
Bu static polymorphism deb ataladi.
Runtime dispatch overheadi bo‘lmasligi mumkin, ammo binary code ko‘payishi ehtimoli bor.
Ad-hoc polymorphism
Bir xil function nomi turli parameter type’lari uchun boshqa implementationga ega.
print(string)
print(integer)
print(date)
Compiler argument type asosida overloadni tanlaydi.
Implicit conversionlar mavjud bo‘lsa, qaysi overload tanlanishi murakkablashishi mumkin.
Parametric polymorphism
Generic code type’dan mustaqil yoziladi.
List<T>
identity<T>(value)
Algorithm Tning aniq turi qanday ekanini bilmasdan ishlaydi.
Type constraint zarur operationlarni belgilaydi.
Masalan, sort qilish uchun elementlar taqqoslanishi kerak.
Monomorphization
Generic function har ishlatilgan concrete type uchun alohida machine code’ga aylantirilishi mumkin.
Afzalliklari:
- static dispatch;
- type-specific optimization;
- boxing talab qilinmasligi.
Kamchiligi — binary hajmi oshishi.
Type erasure
Boshqa runtime generic type ma’lumotning bir qismini compile’dan keyin olib tashlaydi.
Barcha generic instance umumiy representationdan foydalanishi mumkin.
Afzalligi — code size kichik.
Kamchiligi — runtime cast, boxing yoki reflection cheklovi.
Interface polymorphism
Turli class bir interface’ni implementatsiya qiladi.
Storage
├── LocalStorage
├── CloudStorage
└── MemoryStorage
Caller Storage bilan ishlaydi.
Implementation constructor, configuration yoki dependency injection orqali tanlanadi.
Duck typing
Dynamic tillarda objectning class hierarchy’sidan ko‘ra kerakli method mavjudligi muhim.
“Duck kabi yurib, duck kabi ovoz chiqarsa — duck.”
Function objectda read() methodi bo‘lsa ishlatadi.
Afzalligi — moslashuvchanlik.
Kamchiligi — xato runtime’da aniqlanishi mumkin.
Structural typing
Type mosligi nom yoki explicit inheritance orqali emas, mavjud field va methodlar tuzilishi orqali aniqlanadi.
Agar object talab qilingan shaklga ega bo‘lsa, mos hisoblanadi.
Bu duck typingga o‘xshaydi, ammo compile vaqtida tekshirilishi mumkin.
Nominal typing
Type mosligi explicit nom, declaration yoki inheritancega asoslanadi.
Ikki type bir xil fieldlarga ega bo‘lsa ham, alohida nominal type hisoblanadi.
Bu domain chegaralarini aniqroq saqlaydi.
Operator overloading
Bir operator turli type’larda boshqa ma’noga ega bo‘lishi mumkin.
2 + 3
"ab" + "cd"
vector1 + vector2
Operator semantikasi tushunarli bo‘lishi kerak.
Masalan, + operationi mutlaqo kutilmagan side effect bajarsa kodni o‘qish qiyinlashadi.
Coercion polymorphism
Til qiymatni avtomatik boshqa type’ga aylantirib, operatsiyani bajarishi mumkin.
Misol:
integer → floating point
Implicit coercion qulay, ammo precision yo‘qolishi yoki noaniq overload tanlashga sabab bo‘lishi mumkin.
Liskov substitution
Subtype parent contractni buzmasligi kerak.
Agar Bird.fly() parent contract bo‘lsa, ucha olmaydigan qush subtype’i muammo tug‘diradi.
Contract type hierarchy’ga mos model qilinadi.
Polymorphism faqat method nomi bir xil bo‘lishi emas, semantika ham mos bo‘lishidir.
Conditional o‘rniga polymorphism
Kod type nomi bo‘yicha doimiy shart ishlatsa:
if type == "card"
if type == "cash"
if type == "transfer"
har payment type alohida implementation bo‘lishi mumkin.
Ammo variantlar soni kichik, yopiq va oddiy bo‘lsa pattern matching yoki switch tushunarliroq bo‘lishi mumkin.
Visitor va double dispatch
Oddiy dynamic dispatch receiver type’iga qaraydi.
Operation ikki object type’iga bog‘liq bo‘lsa double dispatch kerak bo‘lishi mumkin.
Visitor pattern object hierarchy’ga yangi operation qo‘shadi, ammo yangi subtype qo‘shishni qiyinlashtiradi.
Performance
Dynamic dispatch:
- indirect call;
- branch prediction;
- inlining cheklovi;
- pointer access
overheadiga ega bo‘lishi mumkin.
Ko‘p applicationda bu kichik. Performance-critical loopda profiler bilan o‘lchanadi.
JIT devirtualization va inline cache overheadni kamaytiradi.
Serialization
Polymorphic objectni serialize qilishda haqiqiy subtype ma’lumoti kerak bo‘lishi mumkin.
Format type discriminator saqlaydi:
{
"type": "circle",
"radius": 10
}
Untrusted type nomini to‘g‘ridan-to‘g‘ri arbitrary class yaratishga ishlatish xavfli.
Test
Umumiy contract test barcha implementationlarga qo‘llanadi.
Masalan, har Storage implementation:
- yozilgan qiymatni o‘qiydi;
- yo‘q key uchun bir xil semantika beradi;
- delete contractini saqlaydi.
Implementation-specific testlar qo‘shimcha yoziladi.
Diagnostika
Polymorphism muammosida:
- runtime type;
- selected overload;
- interface contract;
- method resolution;
- type erasure;
- implicit conversion;
- subtype invariant;
- serialization discriminator;
- dynamic proxy
tekshiriladi.
Bir xil method nomi bir xil semantikani avtomatik kafolatlamaydi.
Bog‘liq tushunchalar
Subtype polymorphism, Parametric polymorphism, Dynamic dispatch, Interface, Inheritance, Generics, Duck typing, Method overloading, Liskov substitution