Kas yra SOLID principai?
SOLID principai – tai penkios gairės, padedančios kurti aiškų, lengvai prižiūrimą ir lanksčią programinę įrangą objektinio programavimo srityje. Taikant ir laikantis šių principų, net ir ilgai trunkančių kūrimo projektų metu gaunamas lengvai suprantamas programinės įrangos projektas. Laikantis šių principų, ne tik galima rašyti geresnį kodą, bet ir lengviau prižiūrėti esamą kodą.
Kas yra SOLID principai ir kas juos sukūrė?
Geras šaltinio kodas prasideda nuo taisyklių, programavimo paradigmų ir tinkamo programavimo stiliaus, užtikrinančių efektyvų ir tvarkingą kodą. Būtent tai garantuoja penki SOLID principai, kuriuos suformulavo Robertas C. Martinas, Bertrandas Meyeris ir Barbara Liskov. Laikydamiesi šių principų objektinio programavimo (OOP) kalba, pavyzdžiui, Python ar Java, ne tik rašysite geresnį kodą, bet ir užtikrinsite efektyvesnę kodo priežiūrą, tvarų ir lankstų programinės įrangos dizainą bei didesnį saugumą ilgalaikėje perspektyvoje.
Pavadinimas „SOLID“ simbolizuoja tvirtą programavimo pagrindą, kurį turėtų turėti kiekvienas, norintis išmokti programuoti. Šią santrumpą sukūrė Michaelas Feathersas, paimdamas po pirmąją kiekvieno iš penkių principų raidę:
- Vienos atsakomybės principas: klasė turėtų turėti vieną ir tik vieną priežastį keistis.
- Atvirumo-uždarumo principas: Programinės įrangos elementai (klasės, moduliai, funkcijos ir pan.) turėtų būti atviri išplėtimui, bet uždari modifikavimui.
- Liskov pakeitimo principas: pakaitinės klasės turėtų galėti paveldėti ir įgyvendinti visas viršutinės klasės metodus ir savybes.
- Sąsajų atskyrimo principas: sąsajos neturėtų turėti daugiau metodų, nei reikia klasėms įgyvendinti.
- Priklausomybės apgręžimo principas: klasės neturėtų priklausyti nuo kitų klasių, o nuo sąsajų arba abstrakčių klasių.
Kokios naudos teikia SOLID principai?
Kai nėra taisyklių, įsivyrauja chaosas, ir tai ypač pastebima programavimo metu. Net nedidelės klaidos, netikslumai ir spragos ilgainiui gali paversti gerą šaltinio kodą visiškai netinkamu naudoti, jei jų nepašalinama. Kartais pakanka sudėtingų klasių, kurios apsunkina įgyvendinimą, arba paklasių, kurioms trūksta viršklasių savybių. SOLID principai užtikrina, kad refaktoringo metu tektų taisyti kuo mažiau kodo.
SOLID principai lemia kodo kokybę:
- Aišku, tvarkinga ir patrauklu: programinė įranga ir kodai tampa lengviau suprantami, veiksmingesni ir tiesiog atrodo geriau.
- Lengva prižiūrėti: paprasta ir aiški struktūra leidžia keliems bendradarbiams lengviau prižiūrėti ir valdyti tiek naują, tiek seną kodą.
- Pritaikoma, išplėstina, pakartotinai naudojama: Dėl geresnio suprantamumo, sumažinto sudėtingumo ir atsakomybės bei sumažintos priklausomybės nuo klasių, kodą galima geriau redaguoti, pritaikyti ir išplėsti per sąsajas bei lanksčiai pakartotinai naudoti.
- Mažiau klaidų: švarus kodas su paprasta struktūra reiškia, kad vienos kodo dalies pakeitimai netyčia nepaveiks kitų sričių ar funkcijų.
- Saugus ir patikimesnis: pažeidžiamumų, nesuderinamumų ir klaidų sumažinimas arba pašalinimas pagerina sistemos funkcionalumą ir patikimumą, o tai savo ruožtu pagerina saugumą.
Ką reiškia kiekvienas iš SOLID principų?
SOLID principai yra vieni iš pagrindinių geros programavimo praktikos taisyklių ir turėtų būti žinomi kiekvienam, kuris dirba objektinio programavimo srityje. Toliau išsamiai paaiškinsime kiekvieną principą.
SRP: Vienos atsakomybės principas
Robertas C. Martinas pateikė pirminį šio principo apibrėžimą knygoje „Agile Software Development: Principles, Patterns and Practices“; jis rašė:
„Kiekvienas programinės įrangos modulis turėtų turėti vienintelę priežastį, dėl kurios jį reikėtų keisti“.
Vienos atsakomybės principas (SRP) teigia, kad kiekviena klasė objektiniame programavime (OOP) turėtų turėti tik vieną atsakomybę. Tai reiškia, kad turėtų būti tik viena priežastis, dėl kurios klasę reikėtų keisti. Priešingai nei dažnai manoma, tai nereiškia, kad klasė ar modulis gali turėti tik vieną užduotį. Tai reiškia, kad ji turėtų būti atsakinga tik už konkrečias užduotis, kurios, idealiu atveju, nesidubliuotų su kitomis sritimis.
Atsakomybių sutapimas, pavyzdžiui, kai funkcijos teikiamos įvairiems verslo segmentams, gali turėti įtakos klasės funkcionalumui, jei viename segmente atliekami pakeitimai. Dėl daugybės atsakomybių ir priklausomybių vienintelis pakeitimas vienoje srityje gali sukelti kodavimo klaidas arba pareikalauti papildomų pakeitimų.
Vienos atsakomybės principas (SRP) skirtas kurti nuoseklius modulius, kurių užduotis – atlikti konkrečias, aiškiai apibrėžtas funkcijas arba valdyti objektus. Dėl aiškios, struktūrizuotos struktūros, kurioje yra kuo mažiau priklausomybių ir nepriklausomų įgyvendinimų, pakeitimus ir koregavimus galima atlikti efektyviau, greičiau ir sklandžiau.
Klasės, dar vadinamos objektų tipais, yra pagrindiniai objektinio programavimo (OOP) elementai. Jas galima laikyti objektų brėžiniais, apibrėžiančiais jų savybes, kad programinėje įrangoje būtų galima atkurti realaus pasaulio objektus ir sąvokas. Klasės, dar vadinamos moduliais, dažnai lyginamos su failų tipais.
OCP: Atvirumo ir uždarumo principas
Remiantis Roberto C. Martino ir Bertrando Meyero knyga „Objektinis programinės įrangos kūrimas“, OCP teigia:
„Programinės įrangos elementai (klasės, moduliai, funkcijos ir pan.) turėtų būti atviri plėtrai, bet uždari modifikavimui“.
OCP užtikrina, kad norint įgyvendinti pakeitimus nereikės iš esmės perrašyti programinės įrangos. Jei reikalingi giluminiai kodo pakeitimai, kyla pavojus, kad atsiras nepastebimų klaidų ir kodo trūkumų. Gerai struktūrizuotas kodas turėtų turėti sąsajas, kurias galima naudoti siekiant jį išplėsti papildomomis funkcijomis. Čia raktinis žodis yra klasių paveldėjimas.
Naujas funkcijas ir išplėtimus, kuriuose yra aiškios naujos funkcijos ir metodai, kuriuos reikia įgyvendinti, galima lengvai pridėti prie viršutinės klasės per sąsają, sukuriant pakaitines klases. Tokiu būdu nereikia keisti jau parašyto, stabilaus kodo. Tai supaprastina programinės įrangos priežiūrą ir palaikymą bei žymiai padidina stabilių kodo elementų pakartotinio naudojimo efektyvumą per sąsajas.
LSP: Liskov pakeitimo principas
Remiantis Barbara H. Liskov ir Jeannette M. Wing straipsniu „Elgesio tipizavimas naudojant invariantus ir apribojimus“, LSP teigia, kad:
„Tegul q(x) yra savybė, kurią galima įrodyti apie T tipo objektus x. Tuomet q(y) turėtų būti įmanoma įrodyti apie S tipo objektus y, kur S yra T potipis“.
Tai gali skambėti mįslingai, bet iš tikrųjų tai gana lengva suprasti: susietos arba išplėstos pakaitinės klasės turi veikti taip pat, kaip ir jų viršutinės arba bazinės klasės. Tai reiškia, kad kiekviena pakaitinė klasė turi per paveldėjimą išlaikyti atitinkamos viršutinės klasės savybes, o šios savybės pakaitinėje klasėje negali būti keičiamos. Jos iš esmės turi būti pakeičiamos – iš čia ir kyla pakeitimo principas. Kita vertus, viršutinės klasės gali būti modifikuojamos.
Norėdami tai paaiškinti, panagrinėkime klasikinį Roberto C. Martino pavyzdį apie stačiakampius ir kvadratus. Geometrijos pamokose išmokstame tokį principą: kiekvienas kvadratas yra stačiakampis, bet ne kiekvienas stačiakampis yra kvadratas. Kvadratas ne tik turi stačiakampius kampus, kaip ir stačiakampiai, bet ir visų jo kraštinių ilgis yra vienodas.
Programavimo srityje prielaida, kad panašios ar iš pažiūros identiškos klasės yra susijusios arba viena nuo kitos priklauso, veda prie klaidų, nesusipratimų ir neaiškaus kodo. Dėl šios priežasties programavimo srityje klasė „Rectangle“ nėra kvadratas, o klasė „Square“ nėra stačiakampis. Abi klasės yra atskirtos ir įgyvendinamos atskirai. Nesant integruoto ryšio tarp klasių, nesusipratimai negali sukelti klaidų tarp klasių. Tai padidina saugumą ir stabilumą keičiant įgyvendinimus paklassėse ar viršklasiuose be neigiamų pasekmių.
ISP: Sąsajų atskyrimo principas
Remiantis Roberto C. Martino knyga „Sąsajos atskyrimo principas“, ISP apibrėžiamas taip:
„Klientai neturėtų būti priversti naudotis sąsajomis, kurių jie nenaudoja“.
ISP teigia, kad vartotojai neturėtų būti priversti naudoti jiems nereikalingų sąsajų. Kitaip tariant: siekiant suteikti klientams tam tikrų klasių funkcijas, pagal konkrečius reikalavimus kuriamos naujos, mažesnės sąsajos. Tai neleidžia sąsajoms tapti pernelyg didelėmis ir užtikrina, kad tarp klasių nesusidarytų stiprių priklausomybių. Privalumas yra tas, kad programinę įrangą, kurioje klasės yra atsietos viena nuo kitos ir kurioje yra keletas mažų, pagal konkrečius reikalavimus pritaikytų sąsajų, lengviau prižiūrėti.
DIP: Priklausomybės apgręžimo principas
Pasak Roberto C. Martino, knygoje „Priklausomybės apgręžimo principas“ penktasis ir paskutinis iš SOLID principų yra toks:
„A. Aukšto lygio moduliai neturėtų priklausyti nuo žemo lygio modulių. Abu turėtų priklausyti nuo abstrakcijų. B. Abstrakcijos neturėtų priklausyti nuo detalių“.
DIP užtikrina, kad konkrečios funkcijos ir priklausomybės šaltinio kodo sluoksniuose būtų grindžiamos abstrakčiomis sąsajomis, o ne tiesiogiai viena nuo kitos. Programinės įrangos architektūros paprastai suskirstomos į aukštesnius vartotojo lygmenis ir žemesnius, abstrakčius lygmenis. Logiškai mąstant, galima manyti, kad abstraktus pagrindas daro įtaką aukštesnių sluoksnių veikimui. Tačiau DIP čia įžvelgia potencialią problemą, nes ji sukuria aukštesnių lygmenų priklausomybę nuo žemesnių lygmenų, o tai gali sukelti sunkumų.
Vietoj to, kad aukštesnieji lygiai būtų susieti su žemesniaisiais, aukštesniojo ir žemesniojo lygių klasės turėtų priklausyti nuo abstrakčių, tarpinės grandies sąsajų. Šios sąsajos iš žemesniųjų lygių gauna aukštesniesiems lygiams reikalingas funkcijas ir jas pateikia. Tokiu būdu galima išvengti „iš apačios į viršų“ priklausomybės hierarchijos, kuri laikui bėgant gali sukelti klaidų kode. Tai palengvina modulių pakartotinį naudojimą ir leidžia keisti žemesniojo lygio klases nepaveikiant aukštesniųjų lygių.
Kas nutiks, jei nebus laikomasi SOLID principų?
Sukurti švarų, lengvai suprantamą kodą, kuris palengvintų jo priežiūrą, turėtų būti pagrindinis programinės įrangos kūrimo tikslas. Jei programuotojai nepaiso esminių gairių, pavyzdžiui, SOLID principų, kodas gali smarkiai pablogėti dėl pažeidžiamumų, dubliavimosi, susikaupusių klaidų ir pernelyg didelių priklausomybių. Kraštutiniais atvejais kodas laikui bėgant gali tapti visiškai nenaudojamas. Tai yra didelė problema taikant lanksčią programinės įrangos kūrimo metodiką, kai sudėtingas kodavimo užduotis dažnai atlieka daug žmonių.
Netvarkingo kodo ar netinkamos kodo priežiūros pasekmės yra šios:
- Kodo „kvapas“: kai kodas rašomas nesilaikant reikiamų standartų, gali atsirasti vadinamasis „kodo kvapas“ arba „smirdantis kodas“, o tai gali sukelti funkcines klaidas ir programų nesuderinamumą.
- Kodo puvimas: Jei kodas nėra prižiūrimas ar taisomas per refaktoringą ar brangią kodo peržiūrą, jis gali metaforiškai „supūti“ ir visiškai prarasti savo funkcionalumą. Kitas terminas, apibūdinantis neskaitomą, sudėtingą kodą, yra „spagečių kodas“.
- Saugumo rizika: Kylančios problemos neapsiriboja tik veiklos sutrikimais, sudėtinga priežiūra ir suderinamumo problemomis. Taip pat yra saugumo spragų, kurios suteikia kenkėjiškai programinei įrangai galimybių išnaudoti kodą, įskaitant „zero-day“ pažeidžiamumus.
Kas sukūrė SOLID principus?
SOLID principų ištakos siekia keletą principų, kuriuos 2000 m. savo esė „Design Principles and Design Patterns“ pirmą kartą pristatė Robertas C. Martinas („Dėdė Bobas“), vienas iš agilios programavimo iniciatorių. SOLID principus suformulavo Robertas C. Martinas, Bertrandas Meyeris ir Barbara Liskov. Įsimintiną akronimą išpopuliarino Maiklas Featheris, kuris penkių pagrindinių principų pirmąsias raides išdėstė įsimintina tvarka.
Kokie yra panašūs programavimo principai?
Programinės įrangos kūrimo srityje principai – tai bendrosios arba labai konkrečios gairės ir rekomendacijos, kaip elgtis. Be SOLID principų, kurie buvo sukurti objektinio programavimo reikmėms, kiti švaraus kodo programavimo principai apima:
- DRY principas (Don’t repeat yourself – „Nekartok savęs“) funkcijoms, turinčioms vienintelį, unikalų atvaizdą
- KISS principas (Keep it simple, stupid) – kuo paprastesnis kodas