OOP NIMA?

Bismillah.

Bu maqolada OOP nima ekanligini o’zim bilgancha yoritib berishga harakat qilaman. Maqolada kod misollari TypeScript’da ko’rsatilishiga qaramasdan, u OOP’ni TypeScript’da qanday ishlatish haqida emas, umumiy OOP va unga aloqador tushunchalar haqida.

OOP picture

Kirish

OOP(Object Oriented Programming) – obyekt va class’larga asoslangan dasturlash paradigmasidir. Boshqacharoq qilib aytganda, u biror dastur yasash uchun kod yozganda foydalaniladigan bir uslubdir. Bunday paradigmalar judayam ko’p. Logika va funksiyalarga asoslangan FOP (function oriented programming), protseduralarga asoslangan POP (procedure oriented programming) paradigmalardan farqli ravishda OOP asosan obyekt va classlardan foydalanadi.

OOP va shu kabi boshqa paradigmalar biror tilga bog’lanib qolgan yoki aynan bir tilning xususiyati emas. Aksincha, dasturlash tillari ma’lum paradigmalarni o’zida qo’llab quvvatlaydigan qilib yasaladi. Ba’zi tillar aynan bir paradigmaning ustiga qurilsa, boshqalarida bir nechta paradigmalarni ishlatish imkoniyati bor.

Masalan, Haskell tili tabiatan FOP ustiga qurilgan. Java esa azaldan OOP ni qo’llab quvvatlab kelgan bo’lsa, 8-versiyasidan boshlab, FOP ni ham qo’llab quvvatlashni boshladi. JavaScriptda OOP va FOP uslublarining har ikkalasida ham kod yozishingiz mumkin.

Keling, OOPga chuqurroq kirishdan avval, OOP ga aloqador asosiy tushunchalar mundarijasi bilan tanishib olsak, so’ng har biriga alohida to’xtalamiz.

OOP paradigmasi biror dasturlash tilida qo’llanilishi uchun shu dasturlash tilidagi quyidagi element yoki tip yoki data tiplardan foydalaniladi:

  • Class (required)
  • Object (required)
  • Interface (optional)

Aslida, ba’zi tillarda class yoki interface degan narsani o’zi bo’lmasligi mumkin. Lekin OOP paradigmasi nuqtai nazaridan, ular qiladigan ishni qila oladigan alternative bo’lsa yetarli. Masalan, JavaScriptda 2015-yilga qadar class’lar bo’lmagan. Interface haligacha yo’q. Dasturchilar constructor funksiyalardan class o’rnida foydalanishgan. Xatto biz keyinoq tanishadigan Inheritence uchun JavaScriptda “prototypal inheritence” uslubidan foydalanishgan. Lekin 2015-yilda JavaScriptda, aslida u constructor funksiya uchun “syntax sugar” hisoblansa ham, class’lar paydo bo’ldi.

ES5 constructor function
ES6 class

Syntax sugar – dasturlash tilida ma’lum bir sintaksisni soddaroq, osonroq yoki boshqacharoq variantda yozish imkoniyati

OOP paradigmasining eng asosiy to’rtta ustuni bor. Bular Class va Object’lardagi yoki ular o’rtasidagi turli imkoniyatlarni anglatadi:

  • Abstraction
  • Encapsulation
  • Inheritance
  • Polymorphism

Bundan tashqari, Class va Object’lar orasidagi bir biriga bo’lgan bog’liqlikning ham bir necha turlari bor:

  • Association
  • Dependency
  • Composition
  • Aggregation

Object, Class

Object va class’lar ichiga kirishdan avval Alexander Shvets tomonidan yozilgan “Dive Into Design Patterns” kitobida OOP’ga berilgan ta’rifni keltirib o’tsam:

Object-oriented programming is a paradigm based on the concept of wrapping pieces of data, and behavior related to that data, into special bundles called objects, which are constructed from a set of “blueprints”, defined by a programmer, called classes.

Mazmuni quyidagicha:

Obyektga yo’naltirilgan dasturlash bu, biror ma’lumotni yoki shu ma’lumotga bog’liq harakatlarni, dasturchilar tomonidan yoziladigan “class“lar deb ataluvchi qoliplar orqali yaratiladigan “object” deb ataluvchi to’plamlarga biriktirish konsepsiyasiga asoslangan paradigmadir.

Tushunmagan bo’lsangiz, qaytadan o’qing 🙂

Object – “key” va “value” lardan tashkil topgan data strukturadir. Har bir object’da biror key va shu key’ga biriktirilgan qiymat bo’ladi. Object ichidagi bu qimatlar key’lar orqali boshqariladi.

Object example

Class – o’z ichida ma’lum bir ma’lumotlarni yoki biror amallarni bajaradigan funksiyalarni ushlab turuvchi data tip. OOP’da class’lar object’lar uchun “blueprint” ya’ni chizma yoki qolip vazifasini bajaradi. Har qanday object biror bir class’ning instance’i ya’ni avlodi yoki bolasi hisoblanadi. Class o’z ichida object’da bo’lishi kerak bo’lgan xususiyatlarni va harakatlarni ifoda qiladi, keyin esa, shu class’dan instance olinadi ya’ni object yasaladi. Lekin, shu yerda sizda savol tug’ilishi mumkin. Yuqoridagi rasmda ko’rsatilgan car objecti qaysi classdan instance olayapti? Aslida, u JavaScript’dagi “Object” classidan instance olayapti, lekin bu “syntax sugar” bo’lgani uchun buni yozish shart emas.

Instance – qachonki biror object biror class orqali yasalganda, shu yasalgan object o’sha class uchun instance hisoblanadi.

Keling buni mushuklar misolida chuqurroq ko’ramiz. Tasavvur qiling, biz dasturimizda turli mushuklarga tegishli ma’lumotlarni ishlatmoqchimiz yoki saqlamoqchimiz. Demak bizda judayam ko’p mushuklar bo’lishi mumkin va har bir mushukning deyarli bir xil xususiyatlari bo’ladi, faqat ularning qiymatlari farq qiladi. Masalan, har bir mushukning boshi, ko’zi, oyoqlari bor, bu mushukning xususiyatlaridir(attributes). Shuningdek, mushuk nafas oladi, yuguradi, miyovlaydi, bu uning harakatlaridir(behavior).

Bu yerda har bir mushuk – object, har bir mushuk uchun umumiy xususiyatlar va harakatlarni to’plagan to’plam – class.

Class ichidagi xususiyat(attribute), o’sha classning field’i deyiladi.

Class ichidagi harakat yoki funksiya(behavior), o’sha classning method’i deyiladi.

Class ichidagi barcha field va method’lar umumiy o’sha classning member’lari deyiladi.

Object ichidagi key’larga biriktirilgan data, odatda o’sha objectning state’i hisoblanadi.

Object ichidagi key’larga biriktirilgan funksiyalar, odatda o’sha objectning behavior’i hisoblanadi.

Class va object’lar ichidagi xususiyatlarni belgilashda aniq keyword’lar bo’lsa ham, ko’p dasturchilar class yoki object ichidagi funksiyadan boshqa har qanday qiymat ushlab turuvchi key’ni o’rinma o’rin property, attribute, field, key yoki member deya atashadi. Object va class ichidagi funksiyani o’zida ushlab turuvchi keyni yoki o’sha funksiyani o’zini esa odatda method deyishadi.

Yuqoridagi misoldan ko’rib turganingizdek, Baroqvoy ham, Momiqvoy ham Mushuk classining instance’lari hisoblanadi. Shuning uchun ham har ikkala object ichidagi field va method’lar bir xil, faqatgina qiymatlari farq qiladi.

Abstraction

Abstaction judayam abstrakt tushuncha. Internetda bu bo’yicha maqola qidirisangiz, ko’pchilik bir birinikiga o’xshamagan ta’riflar beradi yoki ko’pincha Encapsulation bilan chalkashtirishadi. Keling soddaroq qilib tushunishga harakat qilamiz. Siz biror dastur uchun class yozganingizda, keyinchalik undan undan olinadigan instance’lar ichida hamma yozilgan funksional kodni tashqariga chiqarishni xoxlamaysiz. Odatda, judayam maxmiy ma’lumotlar yoki shu ma’lumotlar ustida bajariladigan amaliyotlar object ichida qolishi kerak bo’ladi, yoki user’ga faqatgina shu object qiladigan ishlarni manipulate qiladigan memberlarni qoldirib, qolgan implementation’ni yashirish kerak bo’ladi.

Abstraction – class yoki object ichidagi ma’lum member’larni tashqarida ulanish imkoniyatini yashirib, ular private qilib qo’yishga yoki faqatgina ayni context’ga tegishli memberlarni public qoldirib, ichki implementation’ni yashirishga aytiladi.

Yuqoridagi kod misolida, MathLib class’i ichida private va public memberlar e’lon qilingan. JavaScriptda class memeber’ini private qilish uchun uni oldiga # qo’yiladi. Boshqa tillarda odatda private degan keyworddan foydalanishadi.

Ko’rib turganingidek, math objectining public add va minus method’lari chaqirilganda, ular o’z ichida private member’lardagi qiymatlarni qo’shib yoki ayirib berayapti. Lekin to’g’ridan to’g’ri object orqali shu private memberlarga ulanishga urinilganda, bunga ruxsat berilmaydi. Bu abstraction’ning JavaScriptdagi ko’rinishi.

Bu yerda MathLib kutubxonasini user foydalanishi uchun faqatgina add va minus method’lari public qilib, ular ishni bajarishi uchun kerak bo’lgan qolgan member’larni tashqi tomondan yashirilayapti. Chunki uni ishlatayotgan user raqamlarni qayerda saqlanayotgani va uni tekshirish jarayoni implement qilingan kodga ulanishini keragi yo’q.

Abstraction ikki xil bo’ladi:

  • Data abstraction – class ichidagi fieldlarni tashqi tomondan yashirish.
  • Process abstraction – class ichidagi ba’zi method’larni tashqi tomondan yashirish.

Yuqoridagi misolda, #isInteger method’i process abstraction uchun misol bo’sa, #num1 va #num2 data abstraction uchun misoldir.

Class ichidagi member’lar uch xil bo’ladi.

Public – class ichidagi barcha memberlar, dastlabki holatda public bo’ladi. Ya’ni tashqaridan turib ularga ulanish va ularni ishlatish mumkin.

Private – class ichidagi faqatgina shu class o’z ichida ishlata oladigan, tashqaridan ulanishga ruxsat yo’q bo’lgan field yoki method.

Protected – class ichidagi faqatgina shu class yoki shu class’dan meros olgan(inheritence) boshqa class o’z ichida ishlata oladigan, lekin tashqaridan ulanishga ruxsat yo’q bo’lgan field yoki method. Hamma dasturlash tillari class ichida bunday memberlarni e’lon qilish imkoniyatini bermaydi. Masalan JavaScriptda buni imkoni yo’q lekin TypeScriptda bor.

Encapsulation

Encapsulation odatda abstraction bilan birga implement qilinadi. Balki shuning uchun ham ularni farqlab olish biroz qiyindir. Odatda class ichidagi member’lar “by default” public bo’ladi. Ya’ni ularga tashqaridan turib ulanish mumkin. Ularni yashirish uchun tepada gaplashganimizdek private qilishimiz kerak.

Encapsulation jarayonini xuddi blenderning ishlashiga o’xshatish mumkin. Blender ichiga kerakli mahsulotlarni solasiz (input). Keyin esa uning kerakli bir-ikkita tugmasini bosasiz, tamom(interface). Sok tayyor. Lekin siz uning ichidagi maxsus qurilmalar qanday qilib tok kuchlanishini meyorga keltirayotganini, kichik motor qanday qilib parrakni belgilangan tezlikda aylantirayotganini ko’rmaysiz. Ya’ni bularni hammasi blender ichida yashirilgan(abstraction). Sizga esa faqat uni ishlatish uchun kerakli tugmalar berilgan(interface, encapsulation).

Encapsulation – class yoki object ichidagi state va behavior’larni yakdil entity yoki interface’ga biriktirishdir. Yoki boshqacharoq aytganda, class ichidagi member’larga ulanish yoki ishlatish imkoniyatini biror kapsulaga o’rab qo’yishdir. User shu ko’rsatilgan interface orqaligina classni ishlata oladi. Abstraction’dan faqrli tomoni, encapsulation’da memberlar private qilinmaydi. Ya’ni siz faqatgina yashirilgan memberlarni emas, public memberlar’ni ham kapsulaga olishingiz mumkin.

Garchi public member’larni ham kapsulaga olib faqatgina belgilangan interface orqali ulanish imkoniyatini qoldirish mumkin bo’lsa ham, odatda uni abstraction bilan birga ishlatishadi. Ya’ni ichki state va behavior’larning hammasi yashiriladi va ularni ishlatish uchun alohida interface yaratiladi. Interface hammasini jamlab turuvchi bitta yoki bir nechta entity bo’lishi mumkin.

Diqqat! Bu yerda dasturlash tillaridagi interface tipi haqida gapirilmayapti.

Yuqoridagi misoldan ko’rishingiz mumkinki, class ichidagi private field’larni qiymatini o’zgartirish va ularga tashqaridan ulanish uchun JavaScriptdagi maxsus “getter/setter”lardan foydalangan holda interface yasalib, kapsulaga olib qo’yilgan. Bunday qilish orqali ma’lum hollarda data’ga ulanish imkonini kim uchundir taqiqlash yoki qiymatni o’zgartirishdan avval turli validation kabi ishlarni amalga oshirish mumkin.

Bundan tashqari, xuddi blender misolidagi kabi, butun insonni harakatga keltirish uchun uning ichki behavior’lariga to’g’ridan to’g’ri, har biriga ulanmasdan, faqatgina boshla interface’i orqali butun jarayon ishga tushurilayapti. Bu xuddi kalitni burash orqali mashinani o’t oldirishga ham o’xshab ketadi. Siz faqat maxsus interface orqali ichki behavior’larni ishga tushurasiz.

Inheritance

OOP asoslari ichida eng tushunishga osoni menimcha Inheritance bo’lsa kerak. So’zni o’zidan ham ko’rinib turibdiki, u “meros qoldirish” degan ma’noni beradi.

Siz qurayotgan dasturda bir nechta class’larda umumiy memberlar bo’lishi mumkin. Shunaqa payt o’sha umumiy member’larni boshqa class’ga ko’chirib, qolgan class’larni esa unga inherit qilib qo’yilsa, ya’ni bola qilib qo’yilsa, ota class’dagi barcha public va protected member’lar bolaga meros bo’lib o’tadi. Bu bitta kodni qayta qayta ishlatish imkonini beradi.

Super class – meros qoldirayotgan ota class.

Derived class – meros olayotgan bola class.

Odatda, ko’p tillarda, ota classdan barcha protected va public member’lar meros bo’lib o’tadi. Private member’lar meros bo’lib o’tmaydi. Lekin ota class’dagi private memberlarga bola class’dan maxsus yo’llar bilan bog’lanish mumkin. Buning uchun, ota class’da o’sha private member’ga bog’laydigan biror public interface yasash kerak bo’ladi. Bu public interface bolaga meros bo’lib o’tadi va ota classdagi private member’ga reference’ni saqlab qoladi.

JavaScript’da agar derived class’da maxsus constructor funksiya elon qilinmasa, u otasinikidan foydalanadi. Agar derived class o’z constructor funksiyasini e’lon qilsa, uning ichida super class constructorini chaqirib qo’yishi shart. Unga argument yuborish esa ixtiyoriy.

Polymorphism

Rostini aytsam, aynan polymorphism nazariyasini o’rganish uchun ko’p maqola o’qidim. Lekin hammasi chalkash. Hamma har xil fikr aytadi. Design pattern haqidagi kitoblarda ham judayam tushunarli narsa ola olmadim. Lekin qo’ldan kelgancha bilganimni tushuntirishga harakat qilaman.

Polymorphism so’zini bo’laklarga bo’lsak, poly – ko’p, morphism – forma, shakl degan ma’nolarni beradi. Ya’ni ko’p shakllilik yoki ko’p formalik.

Polymorphism va Inheritence o’rtasida bog’liqlik bor. Bazi tillar “Abstract class”larni qo’llab quvvatlaydi. Bunday class’lar odatda instance olish uchun emas, balki, aynan boshqa class’lar undan inherit, meros olishi uchun yasaladi. Ulardan instance olishga ham yo’l qo’yilmaydi. Xo’sh bu o’zi nega kerak?

Abstract class’lar boshqa class’larga bir xil argument va return type’ga ega behavior’ni bir necha xil, yoki ko’plab shakllarda yaratish imkonini beradi.

JavaScript abstract class’larni qo’llab quvvatlamagani uchun, TypeScriptda bir misol yozdim. Bizda Repository nomli abstract class bor va u loyihadagi har qanday ishlatiladigan ORM uchun bo’lishi shart bo’lgan method’larni, ularni argument va return type’ini o’z ichiga olgan. Bu anglatadiki, har xil database’lar bilan ishlaydigan turli biz yasaydigan ORM’lar, shu class’dan inherit qilishga majbur, va super class’dagi har bir method’ni o’z ichida qayta elon qilib, uni implement qilish kerak.

Bizda turli database bilan ishlovchi har xil ORM’lar bo’lishi mumkin, lekin ular bitta abstract class’dan inherit qilishga majbur bo’lgani uchun, ularni hammasini interface’i bir xil bo’ladi. Shuning uchun loyihada database almashishi yoki ORM almashish boshqa kodlarni buzib tashlamaydi.

Bu misolda e’tiborga olish kerak bo’lgan jihat, find va findMany method’larini hamma class database’dan kelib chiqib, turli implement qiladi. Ular interface nuqtai nazaridan bir xil, lekin turli shaklda implement qilingan. Mana shu Polymorphism’ga yaqqol misol bo’ladi.

Yuqoridagi kod ishlamaydi, katta ehtimol bilan. U shunchaki ideani olvolish uchun misol tariqasida yozildi.

Telegram: https://t.me/donishmand23

Mavzuga oid maqolalar