Encapsulation — ma’lumot va unga tegishli amallarni bitta birlikda birlashtirib, ichki state’ga bevosita kirishni cheklash tamoyili.
Encapsulation object yoki module invariantlarini himoya qiladi. Foydalanuvchi ichki fieldlarni istalgan qiymatga o‘zgartirish o‘rniga, belgilangan method va propertylar orqali ishlaydi.
Bu tamoyil faqat private kalit so‘zi bilan cheklanmaydi. Module boundary, closure, package va process ham encapsulation yaratishi mumkin.
State va behavior
Object ichida:
bir joyda saqlanadi.
Masalan, bank hisobida balance manfiy bo‘lmasligi kerak bo‘lsa, barcha o‘zgarish deposit va withdraw orqali bajariladi.
Public va private
Access darajalari tilga qarab farq qiladi:
- public;
- private;
- protected;
- package-private;
- internal.
Public contract tashqi kod uchun.
Private implementation detail.
Private fieldning o‘zi yaxshi encapsulationni kafolatlamaydi. Agar barcha field uchun nazoratsiz getter va setter berilsa, state deyarli ochiq qoladi.
Invariant
Invariant object har to‘g‘ri holatda saqlashi kerak bo‘lgan qoida.
Misollar:
- balance 0dan kichik emas;
- order total itemlar yig‘indisiga teng;
- start date end date’dan oldin;
- collection ichida duplicate yo‘q;
- status transition faqat ruxsat etilgan yo‘nalishda.
Methodlar invariantni tekshiradi.
Setter muammosi
Quyidagi umumiy setter:
setBalance(value)
callerga istalgan qiymat yozish imkonini beradi.
Domain amali:
deposit(amount)
withdraw(amount)
maqsad va qoida haqida ko‘proq ma’lumot beradi.
Bu tell, don’t ask tamoyiliga yaqin.
Information hiding
Encapsulation va information hiding bog‘liq.
Information hiding o‘zgarishi mumkin bo‘lgan qarorlarni tashqi koddan yashiradi.
Masalan, collection array yoki tree ekanini public API bilmaydi.
Implementation o‘zgarsa client code o‘zgarmaydi.
Module encapsulation
Module faqat ayrim function va type’larni eksport qiladi.
Ichki helper, cache va constant tashqaridan ko‘rinmaydi.
Til private modifier bermasa ham, export ro‘yxati encapsulation yaratadi.
Package encapsulation
Katta tizimda package public facade beradi, qolgan classlar internal bo‘ladi.
Boshqa package ichki classga to‘g‘ridan-to‘g‘ri bog‘lanmasa, implementationni xavfsiz o‘zgartirish oson.
Closure
Function ichidagi variable closure orqali saqlanib, faqat qaytarilgan functionlar orqali boshqarilishi mumkin.
createCounter()
→ increment()
→ current()
Tashqi kod counter variable’ga bevosita kira olmaydi.
Bu object-oriented classsiz encapsulation misoli.
Encapsulation va abstraction
Abstraction foydalanuvchiga kerakli modelni ko‘rsatadi.
Encapsulation ichki state va implementationni himoya qiladi.
Misol:
Queue.enqueue()— abstraction;- ichki arrayning private bo‘lishi — encapsulation.
Ikkalasi birga ishlaganda barqaror component hosil bo‘ladi.
Encapsulation va immutability
Immutable object yaratilgandan keyin state’ini o‘zgartirmaydi.
Bu encapsulationni kuchaytiradi:
O‘zgarish uchun yangi object yaratiladi.
Defensive copy
Mutable collectionni getter orqali to‘g‘ridan-to‘g‘ri qaytarish internal state’ni ochadi.
Yechim:
Input collection ham constructor ichida nusxalanishi mumkin.
Object reference
Private fieldga tashqi mutable object reference saqlansa, caller o‘sha objectni keyin o‘zgartirishi mumkin.
Shu sababli ownership va mutability aniqlanadi.
Faqat field private bo‘lishi chuqur encapsulation uchun yetarli emas.
Transaction boundary
Service method bir nechta state o‘zgarishini bitta transaction ichida bajarishi mumkin.
Caller alohida-alohida repository methodlarni noto‘g‘ri tartibda chaqirmaydi.
Bu business invariantni process yoki database darajasida encapsulation qiladi.
Process boundary
Alohida process xotirasi boshqa processdan OS tomonidan himoyalanadi.
Service public API orqali ishlaydi, ichki memory va threadlar bevosita ko‘rinmaydi.
Bu kuchli encapsulation, ammo network latency va partial failure qo‘shadi.
Encapsulationni buzish
Quyidagilar boundaryni chetlab o‘tishi mumkin:
- reflection;
- unsafe pointer;
- public mutable field;
- global variable;
- friend access;
- monkey patching;
- databasega to‘g‘ridan-to‘g‘ri yozish;
- internal package importi.
Ba’zan test yoki framework uchun zarur, ammo couplingni oshiradi.
Anemic model
Faqat getter va setterlardan iborat, barcha business mantiqi tashqi service’da bo‘lgan object anemic domain model deb atalishi mumkin.
Bunday model data container vazifasini bajaradi.
Bu har doim xato emas: DTO va persistence record uchun mos bo‘lishi mumkin. Domain objectda esa invariant ichkarida bo‘lishi foydali.
Serialization
Private fieldlar serialization orqali tashqi formatga chiqishi mumkin.
Serialized schema public contractga aylanishi ehtimoli bor.
Ichki field nomini o‘zgartirish clientni buzmasligi uchun explicit DTO yoki mapping ishlatiladi.
Test
Test faqat private methodga kirishga majbur bo‘lsa, public behavior yoki class mas’uliyati qayta ko‘rib chiqiladi.
Private implementationni testga qattiq bog‘lash refactoringni qiyinlashtiradi.
Public contract va observable behavior test qilinadi.
Thread safety
Encapsulation shared state’ga kirishni bitta object ichida nazorat qilish imkonini beradi.
Object:
orqali concurrency qoidalarini yashiradi.
Caller qaysi lockni olishni bilmasligi kerak.
Diagnostika
Encapsulation muammosida:
- state qayerdan o‘zgargan;
- public mutable reference;
- setter;
- transaction;
- ownership;
- thread;
- serialization;
- reflection;
- internal dependency
tekshiriladi.
Invariant buzilgan bo‘lsa, state’ga olib boradigan barcha kirish yo‘llari topiladi.
Law of Demeter
Object faqat yaqin hamkorlari bilan ishlashi kerak degan tamoyil uzun a.getB().getC().change() zanjirlarini kamaytiradi. Bunday zanjir ichki object graphni tashqariga ochadi va encapsulationni zaiflashtiradi.
Bog‘liq tushunchalar
Information hiding, Abstraction, Invariant, Access modifier, Immutability, Defensive copy, Object, Module, Domain model