SOLID Printsiplari (II-qism)
Tepadagi rasmda, SOLID printsiplarini birinchi bor belgilab bergan odam — Robert Martin (Bob Tog’a).
Liskovning Almashinuv Printsipi
Bu printsipga asosan: ‘Har qanday klassdan meros olgan klasslar, uning hususiyatlarini va hulqini o’zida o’zgarishsiz aks etishi lozim’. Yani, agarda biz ota klassning obyektlari o’rniga, voris klassnikining joylashtirsak, dasturimiz o’zgarishsiz ishlay olishi shart. Ushbu printsip tushunishga bor oz murakkab bo’lishi mumkin, lekin, quyidagi misol unga oydinlik kiritadi degan umiddaman.
class Bird { public funtion fly() { $flyingSpeed = 10; return $this->flyingSpeed; } } class Pigeon extends Bird { public funtion fly() { $flyingSpeed = 16; return $this->flyingSpeed; } } class Ostrich extends Bird { public funtion fly() { return 'Ostrich cannot fly'; } } class BirdControl { private $speed; public function __construct(float $speed) { $this->speed = $speed; } //code... } $bird1 = new Bird(); $bird2 = new Pigeon(); $bird3 = new Ostrich(); $birdStart1 = new BirdControl($bird1->fly()); $birdStart2 = new BirdControl($bird2->fly()); $birdStart3 = new BirdControl($bird3->fly()); // Error!
Ushbu misolda Pigeon (kaptar) va Ostrich (straus) klasslar Bird klassining vorislaridir. Lekin, straus ucha olmaydigan qush, shuning uchun uning fly() funksiyasi qolgan klasslarnikiga qaraganda boshqacha ishlaydi. BirdControl klassida biz har bir qush turining uchish tezligini bilib olmoqdamiz. Ostrich klassining obyektiga kelganda dasturimiz xato haqida habar beradi, chunki BirdControl klassining konstruktori argument sifatida faqat float tipini qabul qilishi mumkin.
Kurib turganingizdek, Ostrich klassi Bird klassining vorisi bo’lsada, uning hususiyatlari ota klassnikidan farq qiladi va bu tepada aytib o’tilgan xatoliklarga olib keladi.
Bunday holatlarning oldini olish uchun, kodimizni Liskovning Almashinuv Printsipiga asoslanib yozishimiz kerak:
class FlyingBird {...} class NonFlyingBird {...} class Pigeon extends FlyingBird {...} class Ostrich extends NonFlyingBird {...}
Shu tarzda, biz har ikki turdagi qushlar uchun alohida ota klass yaratib, voris klasslarning hulqini ota klasslarnikiga mos keluvchi tarzda yozib olishimiz mumkin.
Interfeysni Ajratish Printsipi
Bu printsipga ko’ra: ‘Bir nechta tor ixtisoslashgan interfeyslarlar, bitta katta va ko’p qamrovchi interfeysdan ko’ra qullayroq’.
Bu printsipga amal qilish keraksiz kod yozishdan saqlaydi va dastur shaffofligini ta’minlaydi.
interface IEmployee { public function makeReport(); public function hireStaff(); public function writeCheck(); } class Accountant implements IEmployee { public funtion makeReport() { //code.... } public fuction hireStaff() { throw new Exception('This type of employee cannot hire staff'); } public fuction writeCheck() { throw new Exception('This type of employee cannot write checks'); } }
Yuqoridagi misolda Accountant klass IEmployee interfeysiga amal qilayabti. Shu sabablim klass interfeysdagi barchi funksiyalarni belgilab chiqish kerak, xatto ularni ishlatmasa ham. Bu o’z navbatida, ortiqcha kod yozilishini yuzaga keltirmoqda.
Agar interfeys tuzilishi Interfeysni Ajratish Printsipiga amal qilganda, u quyidagi ko’rinish olishi mumkin edi.
interface IAccountant { public funtion makeReport(); } interface IHR{ public function hireStaff(); } interface ISeller{ public funtion writeCheck(); } class Accountant implements IAccountant { public funtion makeReport() { //code.... } }
Ko’rib turganingizdek, endilikda Accountant klassi IAccountantga yozilga va o’ziga tegishli faqat bitta funksiyani belgilab bermoqda.
Qaramlikni Qayatarish Printsipi
Vikipediyaga ko’ra: ‘Yuqori darajali modullar past darajali modullarga bo’glik bo’lmasligi lozim. Ikki turdagi modullar ham abstraktsiyaga bog’liq bo’lishlari kerak. Abstraktsiyalar detallarga bog’liq bo’lmasligi lozim. Detallar abstraktsiyalarga bog’liq bo’lishlarti kerak’.
class Gasoline80 { public function fillTank() { return 'Filled with Gasoline 80'; } } class Car { public $fuel; public funtion refuel() { $gasoline = new Gasoline80(); $fuel = $gasoline->fillTank(); } }
Ushbu misolda Qaramlikni Qaytarish Printsipini buzish holatini kuzatishimiz mumkin. Ko’rib turganingizdek Car klass Gasoline80 klassiga to’g’ridan to’g’ri bog’liq bo’lib turibdi. Agarda Gasoline80 klassida biron narsa o’zgarsa bu Car klassga ham taasir qiladi va bu yana bir printsip — Ochiqlik-Yopiqlik Printsipiga ham to’g’ri kelmaydi.
Bu muammoni yechish uchun, Gasoline80 klassining o’rniga, shu klassga mos keluvchi abstraktsiya ishlatamiz.
interface IGasoline { public function fillTank(); } class Car { public $fuel; public funtion refuel(IGasoline $gasoline) { $fuel = $gasoline->fillTank(); } }
Endilikda, biz $gasoline parametrga IGasoline interfeysga amal qiluvchi istalgan klassning obyektini qo’yishimiz mumkin.
Umumiy Dasturlash
SOLID Printsiplari (II-qism)