Kaj so načela SOLID?
Načela SOLID obsegajo pet smernic za čisto, vzdržljivo in prilagodljivo kodo v objektno usmerjenem programiranju. Uporaba in upoštevanje teh načel omogoča razumljivo zasnovo programske opreme tudi v dolgoročnih razvojnih projektih. S pomočjo teh načel je mogoče ne le napisati boljšo kodo, temveč tudi lažje vzdrževati obstoječo kodo.
Kaj so načela SOLID in kdo jih je oblikoval?
Dobra izvorna koda temelji na pravilih, programskih paradigmah in primernem programskem slogu, ki omogočajo učinkovito in pregledno kodo. Prav to zagotavljajo pet načel SOLID, ki so jih oblikovali Robert C. Martin, Bertrand Meyer in Barbara Liskov. Če upoštevate ta načela pri objektno usmerjenem programiranju (OOP) z jeziki, kot sta Python ali Java, ne boste le napisali boljše kode, ampak boste tudi zagotovili učinkovitejše vzdrževanje kode, trajnostno in prilagodljivo zasnovo programske opreme ter večjo varnost na dolgi rok.
Ime SOLID simbolizira trdne temelje programiranja, ki jih mora imeti vsak, ki se želi naučiti programirati. Akronim je ustvaril Michael Feathers, ki je za njegovo sestavo uporabil začetnice vseh petih načel:
- Načelo ene odgovornosti: Razred naj ima en in samo en razlog za spremembo.
- Načelo odprtosti-zaprtosti: Programske entitete (razredi, moduli, funkcije itd.) morajo biti odprte za razširitev, vendar zaprte za spremembe.
- Liskovovo načelo nadomestljivosti: Podrazredi morajo biti sposobni podedovati in implementirati vse metode in lastnosti nadrazreda.
- Načelo ločevanja vmesnikov: Vmesniki ne smejo vsebovati več metod, kot jih je potrebno za implementacijo razredov.
- Načelo obrnjene odvisnosti: Razredi ne smejo biti odvisni od drugih razredov, ampak od vmesnikov ali abstraktnih razredov.
Kakšne prednosti prinašajo načela SOLID?
Kjer ni pravil, nastane kaos, kar je še posebej opazno pri programiranju. Že majhne napake, netočnosti in vrzeli lahko dolgoročno povzročijo, da dobra izvorna koda postane popolnoma neuporabna, če se jih ne odpravi. Včasih zadostujejo že zapletene razrede, ki otežujejo implementacijo, ali podrazredi, ki nimajo posameznih lastnosti svojih nadrazredov. Načela SOLID zagotavljajo, da je treba z refaktoriranjem popraviti čim manj kode.
Načela SOLID pri pisanju kode:
- Jasno, pregledno in privlačno: programska oprema in koda sta lažje razumljivi, učinkovitejši in preprosto izgledata bolje.
- Enostavno vzdrževanje: Preprosta in jasna struktura olajša več sodelavcem vzdrževanje in upravljanje tako nove kot stare kode.
- Prilagodljiva, razširljiva, ponovno uporabna: Zaradi izboljšane berljivosti, zmanjšane kompleksnosti in odgovornosti ter zmanjšane odvisnosti od razredov je kodo mogoče bolje urejati, prilagajati in razširjati prek vmesnikov ter jo fleksibilno ponovno uporabiti.
- Manj nagnjena k napakam: Čista koda s preprosto strukturo pomeni, da spremembe v enem delu kode ne vplivajo naključno na druga področja ali funkcije.
- Varno in zanesljivejše: Zmanjšanje ali odprava ranljivosti, nezdružljivosti in napak izboljša funkcionalnost in zanesljivost sistemov, kar posledično izboljša varnost.
Kaj pomeni vsako od načel SOLID?
Načela SOLID sodijo med zlata pravila dobrega programiranja in bi jih moral poznati vsak, ki se ukvarja z objektno usmerjenim programiranjem. V nadaljevanju bomo vsako načelo podrobno razložili.
SRP: Načelo ene odgovornosti
Robert C. Martin je prvotno opredelitev tega načela oblikoval v knjigi »Agile Software Development: Principles, Patterns and Practices«; v njej je zapisal:
»Vsak programski modul naj ima en sam razlog za spremembo.«
Načelo ene odgovornosti (SRP) določa, da naj bi imel vsak razred v objektno usmerjenem programiranju (OOP) le eno odgovornost. To pomeni, da naj bi obstajal le en razlog za spreminjanje razreda. V nasprotju s splošnimi razlagami to ne pomeni, da lahko razred ali modul opravlja le eno nalogo. Pomeni pa, da naj bi bil odgovoren le za določene naloge, ki se v idealnem primeru ne prekrivajo z drugimi področji.
Prekrivanje odgovornosti, na primer ponujanje funkcij za različne poslovne segmente, lahko vpliva na delovanje razreda, kadar pride do sprememb v enem od segmentov. Zaradi večplastnih odgovornosti in številnih odvisnosti lahko že ena sama sprememba na enem področju povzroči napake v kodi ali potrebo po dodatnih spremembah.
Načelo enotne odgovornosti (SRP) je zasnovano za ustvarjanje povezanih modulov, ki so namenjeni izvajanju konkretnih, natančno opredeljenih funkcij ali objektov. Zaradi jasne, strukturirane zasnove z minimalnimi odvisnostmi in neodvisnimi izvedbami je mogoče spremembe in prilagoditve izvajati učinkoviteje, hitreje in brez težav.
Razredi, znani tudi kot tipi objektov, so osrednji elementi objektno usmerjenega programiranja (OOP). Lahko jih razumemo kot načrte za objekte, ki opredeljujejo njihove lastnosti, tako da je mogoče v programski opremi poustvariti objekte in pojme iz resničnega sveta. Razrede, znane tudi kot module, pogosto primerjajo s tipi datotek.
OCP: načelo odprtosti in zaprtosti
Po mnenju Roberta C. Martina in Bertranda Meyera v knjigi »Object Oriented Software Construction« OCP navaja:
»Programske enote (razredi, moduli, funkcije itd.) morajo biti odprte za razširitev, vendar zaprte za spreminjanje«.
OCP zagotavlja, da vam za izvedbo sprememb ni treba na novo pisati jedra programske opreme. Če so potrebne globoke spremembe kode, obstaja tveganje za neopazne napake in »code smells«. Dobro strukturirana koda mora vsebovati vmesnike, prek katerih jo je mogoče razširiti z dodatnimi funkcijami. Ključna beseda pri tem je dedovanje razredov.
Nove funkcije in razširitve z jasno opredeljenimi novimi funkcijami in metodami, ki jih je treba implementirati, je mogoče prek vmesnika v obliki podrazredov enostavno dodati nadrazredu. Na ta način ni treba posegati v že napisano, stabilno kodo. To poenostavi vzdrževanje in posodabljanje programske opreme ter prek vmesnikov znatno izboljša učinkovitost ponovne uporabe stabilnih elementov kode.
LSP: Liskovovo načelo nadomestljivosti
Po mnenju Barbare H. Liskov in Jeannette M. Wing v članku »Behavioral Subtyping Using Invariants and Constraints« LSP navaja, da:
»Naj bo q(x) lastnost, ki jo je mogoče dokazati za objekte x tipa T. Potem naj bi bilo q(y) mogoče dokazati za objekte y tipa S, kjer je S podtip tipa T«.
Morda se sliši skrivnostno, a je v resnici precej enostavno razumeti: povezane ali razširjene podrazrede morajo delovati tako kot njihovi nadrazredi ali osnovni razredi. To pomeni, da mora vsak podrazred prek dedovanja ohraniti lastnosti svojega nadrazreda, te lastnosti pa se v podrazredu ne smejo spreminjati. Načeloma morajo biti zamenljive, od todi izhaja načelo zamenljivosti. Nadrazredi pa se lahko spreminjajo.
Da bi to pojasnili, si oglejmo klasičen primer Roberta C. Martina o pravokotnikih in kvadratih. V pouku geometrije se naučimo naslednje načelo: vsak kvadrat je pravokotnik, vendar ni vsak pravokotnik kvadrat. Kvadrat ima namreč ne le pravokotne stranice, kot jih imajo pravokotniki, ampak so vse njegove stranice tudi enako dolge.
V programiranju domneva, da so podobni ali na videz identični razredi med seboj povezani ali odvisni, vodi do napak, nesporazumov in nejasne kode. Zato v programiranju razred »Rectangle« ni kvadrat, razred »Square« pa ni pravokotnik. Oba sta ločena in implementirana ločeno. Brez integrirane povezave med razredi nesporazumi ne morejo voditi do napak med razredi. To poveča varnost in stabilnost pri zamenjavi implementacij v podrazredih ali nadrazredih brez posledic.
ISP: Načelo ločevanja vmesnikov
Po mnenju Roberta C. Martina v delu »Načelo ločevanja vmesnikov« je ISP opredeljeno kot sledi:
»Stranke ne bi smele biti prisiljene, da so odvisne od vmesnikov, ki jih ne uporabljajo.«
V ISP je navedeno, da uporabniki ne bi smeli biti prisiljeni uporabljati vmesnikov, ki jih ne potrebujejo. Z drugimi besedami: da bi strankam omogočili dostop do funkcij določenih razredov, se oblikujejo novi, manjši vmesniki, prilagojeni konkretnim zahtevam. S tem se prepreči, da bi vmesniki postali preveliki, in zagotovi, da med razredi ne nastanejo premočne odvisnosti. Prednost tega pristopa je, da je programska oprema z ločenimi razredi in več manjšimi vmesniki, prilagojenimi konkretnim zahtevam, lažje vzdrževati.
DIP: Načelo obrnjenih odvisnosti
Po mnenju Roberta C. Martina je »načelo obrnjenih odvisnosti«, peto in zadnje izmed načel SOLID, naslednje:
„A. Moduli višje ravni ne smejo biti odvisni od modulov nižje ravni. Oba morata biti odvisna od abstrakcij. B. Abstrakcije ne smejo biti odvisne od podrobnosti.“
Načelo DIP zagotavlja, da se posamezne funkcionalnosti in odvisnosti znotraj plasti izvorne kode opirajo na abstraktne vmesnike, ne pa neposredno druga na drugo. Programske arhitekture so običajno razdeljene na višje uporabniške ravni in nižje, bolj abstraktne ravni. Logično bi bilo misliti, da abstraktna podlaga vpliva na delovanje zgornjih plasti. Vendar pa načelo DIP tukaj opozarja na potencialno težavo, saj ustvarja odvisnosti višjih ravni od nižjih, kar lahko povzroči težave.
Namesto da bi višje ravni povezovali z nižjimi, morajo razredi na višjih in nižjih ravneh temeljiti na abstraknih, vmesnih vmesnikih. Ti vmesniki pridobivajo funkcionalnosti, potrebne na višjih ravneh, iz nižjih ravni in jih dajo na voljo. Na ta način se lahko izognemo hierarhiji odvisnosti od spodaj navzgor, ki lahko sčasoma privede do napak v kodi. To olajša ponovno uporabo modulov in omogoča spremembe v nižjih razredih, ne da bi to vplivalo na višje ravni.
Kaj se zgodi, če se načela SOLID ne upoštevajo?
Pisanje čistega in berljivega kode, ki poenostavlja vzdrževanje, bi moralo biti glavni cilj pri razvoju programske opreme. Če razvijalci spregledajo ključna navodila, kot so načela SOLID, se lahko kakovost kode močno poslabša zaradi ranljivosti, podvajanja, nakopičenih napak in prekomernih odvisnosti. V skrajnih primerih lahko koda sčasoma postane neuporabna. To je pomemben problem pri agilnem razvoju programske opreme, kjer na zahtevnih programskih nalogah pogosto dela več ljudi hkrati.
Med posledice neurejene kode ali slabe vzdrževanja kode spadajo:
- Zaznamek slabe kode: Kadar koda ni napisana v skladu z ustreznimi standardi, lahko to povzroči »zaznamek slabe kode« ali »smrdljivo kodo«, kar vodi do funkcionalnih napak in nezdružljivih programov.
- Code rot: Če koda ni vzdrževana ali popravljena z refaktoriranjem ali dragim pregledom kode, lahko koda v prenesenem pomenu »gnije« in popolnoma izgubi svojo funkcionalnost. Drugi izraz za neberljivo, zapleteno kodo je spaghetti code.
- Varnostna tveganja: Težave, ki se pojavijo, niso omejene na izpade, zapleteno vzdrževanje in težave z združljivostjo. Obstajajo tudi varnostne vrzeli, ki zlonamerni programski opremi omogočajo izkoriščanje kode, vključno z izkoriščanjem ranljivosti zero-day.
Kdo je oblikoval načela SOLID?
Izvor načel SOLID sega v več načel, ki jih je leta 2000 v svojem eseju z naslovom »Design Principles and Design Patterns« prvi predstavil Robert C. Martin (»Uncle Bob«), eden od pobudnikov agilnega programiranja. Načela SOLID so oblikovali Robert C. Martin, Bertrand Meyer in Barbara Liskov. Ujemljivo kratico je populariziral Michael Feathers, ki je začetne črke petih bistvenih načel preuredil v zapomljiv vrstni red.
Katera podobna načela programiranja obstajajo?
V razvoju programske opreme so načela splošna ali zelo konkretna navodila in priporočila za delovanje. Poleg načel SOLID, ki so bila razvita za objektno usmerjeno programiranje, med druga načela za pisanje čiste kode spadajo:
- Načelo DRY (Don’t repeat yourself – Ne ponavljaj se) za funkcije z eno samo, edinstveno predstavitvijo
- Načelo KISS (Keep it simple, stupid) za kodo, sestavljeno čim bolj preprosto