SOLID-print­sii­bid koosnevad viiest tavast ja juhisest, mis aitavad ob­jek­ti­põ­hi­sel prog­ram­mee­ri­misel kirjutada puhast, hool­da­ta­vat ja paind­likku koodi. Print­sii­pide ra­ken­da­mine ja järgimine võimaldab hõlpsasti mõis­te­ta­vat tark­va­ra­di­saini ka pikkade aren­dus­pe­rioo­dide vältel. Nii ei kirjuta te mitte üksnes paremat koodi, vaid saate ka ole­mas­ole­vat koodi paremini hooldada.

Mis on SOLID-print­sii­bid?

Hea läh­te­koodi aluseks on reeglid, prog­ram­mee­ri­mis­pa­ra­digmad ja sobiv prog­ram­mee­ri­misstiil, mis tagavad tõhusa ja puhta koodi. Just selle eest hoo­lit­se­vad viis SOLID-print­siipi, mille ku­junda­sid Robert C. Martin, Bertrand Meyer ja Barbara Liskov. Kui järgite ob­jek­to­rien­tee­ri­tud prog­ram­mee­ri­mises (OOP) selliste keeltega nagu Python või Java teatud print­siipe, kirjutate mitte ainult paremat koodi, vaid tagate pikas pers­pek­tii­vis ka tõhusama koo­di­hool­duse, jät­ku­suut­liku ja paindliku tark­va­ra­di­saini ning suurema tur­va­li­suse.

Nimetus SOLID tähistab ühelt poolt kindlat aren­dus­alust kõigile, kes soovivad õppida prog­ram­mee­rima. Teiselt poolt koosneb lühend viie print­siibi esi­täh­te­dest (Michael Feathersi järgi):

  • Single Res­pon­si­bi­lity Principle (ai­nu­vas­tu­tuse printsiip): klassil tohib olla ainult üks vastutus (mitte ülesanne) ja ainult üks põhjus muut­miseks!
  • Open Closed Principle (avatuse ja suletuse printsiip): klassid peavad olema avatud laien­dus­tele ja suletud muu­da­tus­tele!
  • Liskov Subs­ti­tu­tion Principle (Liskovi asen­dus­print­siip): alam­klas­sid peavad suutma pärida ja imp­le­men­tee­rida kõik ülem­klassi meetodid ja omadused!
  • Interface Seg­re­ga­tion Principle (liideste eral­da­mise printsiip): liidesed ei tohi sisaldada rohkem meetodeid, kui imp­le­men­tee­rivad klassid vajavad!
  • De­pen­dency Inversion Principle (sõl­tu­vuste pööramise printsiip): klassid ei tohi sõltuda teistest klas­si­dest, vaid lii­des­test või abst­rakt­se­test klas­si­dest!

Millised on SOLID-print­sii­pide eelised?

Seal, kus reegleid ei ole, tekib kaos – prog­ram­mee­ri­mises ilmneb see eriti selgelt. Juba väikesed vead, eba­täp­su­sed ja lüngad võivad „ravimata“ jäädes muuta hea läh­te­koodi pikemas pers­pek­tii­vis täiesti ka­su­tus­kõlb­ma­tuks. Selleks ei ole vaja palju: piisab juba kee­ru­ka­test klas­si­dest, mis ras­ken­da­vad imp­le­men­tee­ri­mist, või alam­klas­si­dest, millel puuduvad mõned nende ülem­klas­side omadused. SOLID-print­sii­bid hoo­lit­se­vad selle eest, et koodi oleks vaja re­fak­to­ree­ri­mise teel või­ma­li­kult vähe parandada.

SOLID-print­sii­pide ra­ken­da­mise eelised on järgmised:

  • Selge, puhas ja ilus: tarkvara ja kood on hõlp­sa­mini mõistetav, paremini jälgitav, tõhusam ja lihtsalt ilusam.
  • Lihtne hooldada: üle­vaat­liku ja selge struk­tuuri tõttu on nii uut kui ka aja jooksul kasvanud koodi lihtsam hooldada ka siis, kui sellega tegeleb mitu inimest.
  • Ko­han­da­tav, laien­da­tav, taas­ka­su­ta­tav: parem loetavus, väiksem keerukus ja vas­tu­tuste hulk ning klasside vä­hen­da­tud sõl­tu­vu­sed või­mal­da­vad koodi paremini muuta, kohandada, liideste kaudu laiendada ja paind­li­kult taas­ka­su­tada.
  • Vähem veaohtlik: lihtsa struk­tuuriga puhta koodi puhul ei mõjuta ühe osa muutmine soo­vi­ma­tult teisi valdkondi ega funkt­sioone.
  • Tur­va­li­sem ja usal­dus­väär­sem: väiksema arvu nõrkuste, ühil­du­ma­tuste või vigade tõttu – või nende täieliku puudumise tõttu – paraneb süs­teemide funkt­sio­naal­sus ja töö­kind­lus ning seega ka tur­va­li­sus.

SOLID-print­sii­bid üle­vaat­li­kult

SOLID-print­sii­bid kuuluvad hea prog­ram­mee­ri­mise kuld­reeg­lite hulka ja peaksid seetõttu olema tuttavad kõigile, kes ob­jek­ti­põ­hise prog­ram­mee­ri­mi­sega tegelevad. Tut­vus­tame viit print­siipi ük­sik­as­ja­li­kult.

SRP: Single Res­pon­si­bi­lity Principle (ai­nu­vas­tu­tuse printsiip)

Robert C. Martini algne määratlus teoses „Agile Software De­ve­lop­ment: Pri­nciples, Patterns and Practices“ ütleb:

Tsitaat

„Klassi muut­miseks ei tohiks kunagi olla rohkem kui üks põhjus.“

SRP ütleb, et igal OOP-i klassil tohib olla ainult üks vastutus. Seega tohib klassi muut­miseks olla ainult üks põhjus. Vas­tu­pi­di­selt levinud väära­ru­saama­dele ei tähenda see, et klassil või igal moodulil tohiks olla täpselt üks ülesanne. Küll aga peaks see kandma vastutust ainult kindlate üles­an­nete eest, mis võimaluse korral ei kattu teiste vald­kon­da­dega.

Vas­tu­tuste kattumine, näiteks funkt­sioo­nide pakkumine eri äri­vald­kon­da­dele, võib ühe valdkonna muutmisel mõjutada kogu klassi talitlust. Kui vastutusi on rohkem kui üks ja sõltuvusi liiga palju, võib muudatus ühes vald­kon­nas tuua kaasa mitu järgnevat muudatust või koodivigu.

Seega on SRP eesmärk arendada sidusaid mooduleid, mis vas­tu­ta­vad selgelt mõis­te­tava ülesande või selgelt mää­rat­le­tud objektide eest. Selgelt struk­tu­ree­ri­tud kuju, vä­hen­da­tud sõl­tu­vuste ja lah­ti­si­des­ta­tud imp­le­men­tat­sioo­nide tõttu saab hi­li­se­maid muudatusi ja kohandusi teha liht­sa­malt, kiiremini ja tü­sis­tus­teta.

Märkus

Klassid, tuntud ka ob­jek­ti­tüü­pi­dena, on ob­jek­to­rien­tee­ri­tud prog­ram­mee­ri­mise (OOP) keskne element. Neid võib mõista ehi­tus­plaa­nina, mis sisaldab atribuute reaalsete, sarnaste ja konst­ruee­ri­ta­vate objektide ku­ju­ta­miseks tark­va­ra­ob­jek­ti­dena. Seetõttu võr­rel­dakse klasse, mida tuntakse ka moo­du­li­tena, sageli fai­li­tüü­pi­dega.

OCP: Open Closed Principle (avatuse ja suletuse printsiip)

Bertrand Meyeri ja Robert C. Martini järgi teoses „Object Oriented Software Const­ruc­tion“ ütleb OCP:

Tsitaat

„Tark­va­ra­ük­su­sed (klassid, moodulid, funkt­sioo­nid jne) peaksid olema avatud laien­dus­tele, kuid suletud muu­da­tus­tele.“

OCP hoolitseb selle eest, et teil ei oleks muu­da­tuste ra­ken­da­miseks vaja tarkvara tuuma ümber kirjutada. Kui on vaja põh­ja­likke koo­di­muu­da­tusi, tekib oht peente vigade ja code smell’i tekkeks. Hästi struk­tu­ree­ri­tud koodi peaks saama varustada lii­des­tega, mille kaudu seda täien­da­vate funkt­sioo­ni­dega laiendada. Märksõna on siinkohal klasside pärimine.

Uusi funkt­sioone ja laiendusi koos üle­vaat­like uute funkt­sioo­nide ja mee­to­di­tega, mis tuleb imp­le­men­tee­rida, saab liidese kaudu hõlpsasti ülem­klassi külge haakida alam­klas­side kujul. Nii ei pea te juba kir­ju­ta­tud sta­biil­ses koodis „sorima“. See liht­sus­tab prog­rammide hooldust ja muudab sta­biil­sete koo­di­ele­men­tide taas­ka­su­ta­mise liideste kaudu märksa tõ­hu­samaks.

LSP: Liskov Subs­ti­tu­tion Principle (Liskovi asen­dus­print­siip)

Barbara H. Liskovi ja Jeannette M. Wingi järgi teoses „Be­ha­vio­ral Subtyping Using In­va­riants and Const­raints“ ütleb LSP:

Tsitaat

„Olgu q (x) tüüpi T objekti x omadus, siis peaks q (y) kehtima kõigi tüüpi S objektide kohta, kusjuures S on tüübi T alamtüüp.“

See, mis es­ma­pil­gul krüp­ti­li­selt kõlab, on te­ge­li­kult lihtsasti mõistetav: seotud või laien­da­tud alam­klas­sid peavad töötama nagu nende ülem- või baas­klas­sid. See tähendab, et iga alamklass peab pärimise teel saama oma ülem­klassi omadused ja need omadused ei tohi alam­klas­sis muutuda. Need peavad olema põ­hi­mõt­te­li­selt asen­da­ta­vad – sellest ka nimetus asen­dus­print­siip. Ülem­klasse seevastu tohib muu­da­tus­tega mo­di­fit­see­rida.

Näit­li­kus­ta­miseks sobib Robert C. Martini klas­si­ka­line rist­kü­liku ja ruudu näide. Geo­meet­ria­tun­nis kehtib tõdemus: iga ruut on ristkülik, kuid mitte iga ristkülik ei ole ruut. Ruudul on rist­kü­li­kute ülem­klas­siga ühine omadus „täis­nurk­sed küljed“, kuid lisaks on tal omadus „võrdse pikkusega küljed“.

Prog­ram­mee­ri­mises on olukord teine: siin viib eeldus, et sarnased või näiliselt identsed klassid on omavahel seotud või üks­tei­sest sõltuvad, vigadeni, aru­saa­ma­tus­teni ja ebaselge koodini. Just seetõttu ei ole prog­ram­mee­ri­mises klass „ristkülik“ ruut ega klass „ruut“ ristkülik. Mõlemad „lah­ti­si­des­ta­takse“ ja imp­le­men­tee­ri­takse eraldi. Ilma klasside vahelise sis­se­ehi­ta­tud seoseta ei saa aru­saa­ma­tus põh­jus­tada klas­si­de­üle­seid vigu. See suurendab tur­va­li­sust ja sta­biil­sust imp­le­men­tat­sioo­nide va­he­ta­misel alam- või ülem­klas­si­des, ilma et sellel oleks kõr­val­mõ­jusid.

ISP: Interface Seg­re­ga­tion Principle (liideste eral­da­mise printsiip)

ISP on Robert C. Martini järgi teoses „The Interface Seg­re­ga­tion Principle“ mää­rat­le­tud nii:

Tsitaat

„Kliente ei tohiks sundida sõltuma lii­des­test, mida nad ei kasuta.“

ISP ütleb, et ka­su­ta­jaid ei tohiks sundida kasutama liideseid, mida nad ei vaja. Teisisõnu: selleks et teha klien­ti­dele kät­te­saa­da­vaks kindlate klasside funkt­sioo­nid, ko­han­da­takse uued, väiksemad liidesed konk­reet­se­tele nõuetele. Nii saab vältida liideste liiga suureks paisumist ja tugevate sõl­tu­vuste teket klasside vahel. Eelis: lah­ti­si­des­ta­tud klasside ja mitme väikese, konk­reet­se­tele nõuetele ko­han­da­tud liidesega tarkvara on lihtsam hooldada.

DIP: De­pen­dency Inversion Principle (sõl­tu­vuste pööramise printsiip)

Viies ja viimane SOLID-printsiip kõlab Robert C. Martini järgi teoses „The De­pen­dency Inversion Principle“ nii:

Tsitaat

„A. Kõrgema taseme moodulid ei tohiks sõltuda madalama taseme moo­du­li­test. Mõlemad peaksid sõltuma abst­rakt­sioo­ni­dest. B. Abst­rakt­sioo­nid ei tohiks sõltuda de­tai­li­dest. Detailid peaksid sõltuma abst­rakt­sioo­ni­dest.“

DIP hoolitseb selle eest, et konk­reet­sed funkt­sioo­nid ja sõl­tu­vu­sed läh­te­koodi tasemetel toetuksid abst­rakt­se­tele lii­des­tele, mitte üks­tei­sele. Sel­gi­tu­seks: tark­va­ra­ar­hi­tek­tuure võib jämedalt jagada kõr­ge­ma­teks ka­su­ta­ja­ta­se­me­teks ning ma­da­la­ma­teks või sü­ga­va­ma­teks abst­rakt­se­teks ta­se­me­teks. Puht­loo­gi­li­selt võiks eeldada, et abst­raktne alus määrab kõrgemate tasemete käitumise. DIP näeb selles aga veaohtu, kuna nii tekivad kõrgemate tasemete sõl­tu­vu­sed sü­ga­va­ma­test ta­se­me­test.

Selle asemel et siduda kõrgemad tasemed ma­da­la­ma­tega, peaksid nii kõrgete kui ka madalate tasemete klassid sõltuma abst­rakt­se­test va­he­peal­se­test lii­des­test. Liidesed pärivad ma­da­la­ma­telt ta­se­me­telt funkt­sio­naal­su­sed, mida kõr­ge­ma­tel tasemetel vajatakse, ja teevad need kät­te­saa­da­vaks. Nii saab vältida sõl­tu­vus­test koosnevat „alt-üles-hie­rar­hiat“, mis võib aja jooksul koodis vigu põh­jus­tada. See liht­sus­tab moodulite taas­ka­su­ta­mist ja võimaldab muuta ma­da­la­maid klasse ilma kõrgemaid tasemeid mõ­ju­ta­mata.

Mis juhtub siis, kui SOLID-print­siipe ei järgita?

Iga tark­va­raa­ren­duse eesmärk peaks olema ilus, hõlpsasti loetav kood, mis teeb hoolduse või­ma­li­kult lihtsaks. Kui aga sellised juhised nagu SOLID-print­sii­bid ununevad, vananeb kood nõrkuste, liiasuste, kuhjunud vigade ja liiga paljude sõl­tu­vuste tõttu väga halvasti. Halvimal juhul muutub kood aja jooksul ka­su­tus­kõlb­ma­tuks. Eriti suur probleem on see agiilses tark­va­raa­ren­duses, sest seal töötab keeruka koodiga ta­va­li­selt palju inimesi.

Ebapuhta koodi või halva koo­di­hool­duse ta­ga­jär­gede hulka kuuluvad:

  • Code smell: aja jooksul kuhjunud nõrkused ebapuhtas koodis, tuntud kui code smell ehk „haisev kood“, põh­jus­ta­vad ta­lit­lus­vigu ja ühil­du­ma­tuid programme.
  • Code rot: kui koodi ei hooldata ega parandata re­fak­to­ree­ri­mise või kuluka koo­di­üle­vaa­tuse abil, võib kood ülekantud tä­hen­duses „mädaneda“ ja kaotada täie­li­kult oma talitluse. Loetamatu, segase koodi kohta ka­su­ta­takse ka nimetust spa­ge­ti­kood.
  • Tur­va­au­gud: ta­ga­jär­jeks ei ole mitte ainult tõrked, keeruline hooldus või ühil­du­ma­tu­sed, vaid ka tur­va­au­gud, mis annavad pa­ha­va­rale võimaluse ära­ka­su­tus­teks ja nullpäeva-rünneteks.

Kes töötas SOLID-print­sii­bid välja?

SOLID-print­sii­pide päritolu ulatub mitme print­sii­bini, mille tutvustas es­ma­kord­selt agiilse prog­ram­mee­ri­mise üks al­ga­ta­jaid Robert C. Martin („Uncle Bob“) oma 2000. aastal ilmunud essees „Design Pri­nciples and Design Patterns“. Viiest SOLID-print­sii­bist koosneva rühma ku­junda­sid Robert C. Martin, Bertrand Meyer ja Barbara Liskov. Meel­de­jää­vat, print­sii­pide viiest esitähest koosnevat akronüümi levitas omakorda Michael Feathers, kes järjestas viis olulist print­siipi ümber.

Millised sarnased prog­ram­mee­ri­mis­print­sii­bid veel on?

Tark­va­raa­ren­duses tä­his­ta­vad print­sii­bid üldisi või väga konk­reet­seid suuniseid ja te­ge­vus­soo­vi­tusi, nagu ka SOLID-print­sii­bid, mis pakuvad print­sii­pide komplekti ob­jek­to­rien­tee­ri­tud prog­ram­mee­ri­mise paradigma jaoks. Teised puhta koodi prog­ram­mee­ri­mis­print­sii­bid on näiteks:

  • DRY-printsiip (Don’t repeat yourself) – funkt­sioo­nid ühe ainsa, üheselt mõis­te­tava esitusega
  • KISS-printsiip (Keep it simple, stupid) – või­ma­li­kult lihtsalt üles ehitatud kood
Mine peame­nüüsse