Mitkä ovat SOLID-periaatteet?
SOLID-periaatteet koostuvat viidestä ohjeesta, jotka koskevat puhtaita, ylläpidettäviä ja joustavia koodeja objektipohjaisessa ohjelmoinnissa. Periaatteiden soveltaminen ja noudattaminen johtaa helposti ymmärrettävään ohjelmistosuunnitteluun pitkien kehityskausien aikana. Näiden periaatteiden avulla voidaan paitsi kirjoittaa parempia koodeja, myös ylläpitää olemassa olevia koodeja helpommin.
Mitkä ovat SOLID-periaatteet ja kuka ne on kehittänyt?
Hyvä lähdekoodi alkaa säännöistä, ohjelmointiparadigmoista ja sopivasta ohjelmointityylistä, jotta koodi on tehokasta ja siistiä. Juuri tämän takaavat Robert C. Martinin, Bertrand Meyerin ja Barbara Liskovin luomat viisi SOLID-periaatetta. Noudattamalla näitä periaatteita olio-ohjelmoinnissa (OOP) kielillä kuten Python tai Java, kirjoitat paitsi parempaa koodia, myös varmistat tehokkaamman koodin ylläpidon, kestävän ja joustavan ohjelmistosuunnittelun sekä paremman turvallisuuden pitkällä aikavälillä.
Nimi SOLID edustaa vankkaa ohjelmoinnin perustaa, joka jokaisen ohjelmointia oppivan tulisi hallita. Akronyymi on Michael Feathersin luoma, ja se muodostuu viiden periaatteen ensimmäisistä kirjaimista:
- Yhden vastuualueen periaate: Luokalla tulisi olla yksi ja vain yksi syy muutokseen.
- Avoin-suljettu-periaate: Ohjelmistoentiteetit (luokat, moduulit, funktiot jne.) tulisi olla avoimia laajennuksille, mutta suljettuja muutoksille.
- Liskovin korvausperiaate: Alaluokkien tulisi pystyä perimään ja toteuttamaan kaikki yläluokan menetelmät ja ominaisuudet.
- Rajapinnan erottamisen periaate: Rajapinnat eivät saa sisältää enempää menetelmiä kuin luokkien toteuttaminen edellyttää.
- Riippuvuuden kääntämisen periaate: Luokkien ei tulisi olla riippuvaisia muista luokista, vaan rajapinnoista tai abstrakteista luokista.
Mitä etuja SOLID-periaatteet tarjoavat?
Jos sääntöjä ei ole, seurauksena on kaaos, mikä tulee erityisen selvästi esiin ohjelmoinnissa. Pienetkin virheet, epätarkkuudet ja puutteet voivat tehdä hyvästä lähdekoodista täysin käyttökelvottoman pitkällä aikavälillä, jos niitä ei korjata. Joskus riittää, että on monimutkaisia luokkia, jotka vaikeuttavat toteutusta, tai alaluokkia, joilta puuttuu yläluokkien yksittäisiä ominaisuuksia. SOLID-periaatteet varmistavat, että refaktorointia tarvitaan mahdollisimman vähän koodin korjaamiseen.
SOLID-periaatteet luovat koodin:
- Selkeä, siisti ja houkutteleva: Ohjelmistot ja koodit ovat helpommin ymmärrettäviä, tehokkaampia ja yksinkertaisesti näyttävät paremmalta.
- Helppo ylläpitää: Suora ja selkeä rakenne helpottaa useiden yhteistyökumppaneiden työtä sekä uuden että vanhan koodin ylläpidossa ja hallinnassa.
- Mukautettavissa, laajennettavissa, uudelleenkäytettävissä: Parannetun luettavuuden, vähentyneen monimutkaisuuden ja vastuiden sekä luokista riippuvuuden vähenemisen ansiosta koodia voidaan paremmin muokata, mukauttaa ja laajentaa rajapintojen kautta sekä käyttää joustavasti uudelleen.
- Vähemmän virhealtis: Puhdas koodi, jolla on yksinkertainen rakenne, tarkoittaa, että muutokset koodin yhteen osaan eivät vaikuta vahingossa muihin alueisiin tai toimintoihin.
- Turvallisempi ja luotettavampi: Haavoittuvuuksien, yhteensopimattomuuksien ja virheiden vähentäminen tai poistaminen parantaa järjestelmän toimivuutta ja luotettavuutta, mikä puolestaan parantaa turvallisuutta.
Mitä kukin SOLID-periaate tarkoittaa?
SOLID-periaatteet kuuluvat hyvän ohjelmoinnin kultaisiin sääntöihin, ja niiden tulisi olla tuttuja kaikille, jotka työskentelevät objektipohjaisen ohjelmoinnin parissa. Seuraavassa selitämme kunkin periaatteen yksityiskohtaisesti.
SRP: Yhden vastuualueen periaate
Robert C. Martin loi tämän periaatteen alkuperäisen määritelmän teoksessaan ”Agile Software Development: Principles, Patterns and Practices” (Ketterä ohjelmistokehitys: periaatteet, mallit ja käytännöt) kirjoittamalla:
”Jokaisella ohjelmistomoduulilla tulisi olla yksi ja ainoa syy muutokseen”.
Yhden vastuualueen periaate (SRP) tarkoittaa, että jokaisella luokalla olio-ohjelmoinnissa (OOP) tulisi olla vain yksi vastuualue. Tämä tarkoittaa, että luokkaa tulisi muuttaa vain yhdestä syystä. Toisin kuin yleisesti tulkitaan, tämä ei tarkoita, että luokalla tai moduulilla voi olla vain yksi tehtävä. Se tarkoittaa pikemminkin, että sen tulisi olla vastuussa vain tietyistä tehtävistä, jotka eivät ihannetapauksessa ole päällekkäisiä muiden alueiden kanssa.
Vastuualueiden päällekkäisyys, kuten eri liiketoimintasegmenttien toimintojen tarjoaminen, voi vaikuttaa luokan toiminnallisuuteen, kun yhteen segmenttiin tehdään muutoksia. Useiden vastuualueiden ja lukuisien riippuvuuksien hallinta voi aiheuttaa yhden alueen muutoksen seurauksena koodivirheitä tai lisämuutosten tarpeen.
Yhden vastuualueen periaate (SRP) on suunniteltu luomaan yhtenäisiä moduuleja, joilla on tarkasti määritellyt toiminnot tai kohteet. Sen selkeän, jäsennellyn rakenteen, vähäisten riippuvuuksien ja itsenäisten toteutusten ansiosta muutokset ja säätöjä voidaan tehdä tehokkaammin, nopeammin ja sujuvammin.
Luokat, joita kutsutaan myös objektityypeiksi, ovat objektisuuntautuneen ohjelmoinnin (OOP) keskeisiä elementtejä. Niitä voidaan pitää objektien suunnitelmina, joissa kuvataan niiden ominaisuudet, jotta todelliset objektit ja käsitteet voidaan luoda uudelleen ohjelmistoissa. Luokkia, joita kutsutaan myös moduuleiksi, verrataan usein tiedostotyyppeihin.
OCP: Avoimen ja suljetun periaate
Robert C. Martinin ja Bertrand Meyerin teoksessa Object Oriented Software Construction OCP määritellään seuraavasti:
”Ohjelmistoentiteetit (luokat, moduulit, toiminnot jne.) tulisi olla avoimia laajennuksille, mutta suljettuja muutoksille”.
OCP varmistaa, että muutosten toteuttamiseksi ei tarvitse kirjoittaa ohjelmiston ydintä uudelleen. Jos tarvitaan perusteellisia koodimuutoksia, on olemassa riski hienovaraisten virheiden ja koodiongelmien syntymisestä. Hyvin jäsennelty koodi tarjoaa rajapintoja, joita voidaan käyttää sen laajentamiseen lisätoiminnoilla. Avainsana tässä on luokkien periytyminen.
Uudet ominaisuudet ja laajennukset, joihin on lisättävä selkeitä uusia toimintoja ja menetelmiä, voidaan helposti lisätä superluokkaan alaluokkien muodossa olevan rajapinnan kautta. Näin sinun ei tarvitse muuttaa kirjoitettua, vakaata koodia. Se yksinkertaistaa ohjelmistojen ylläpitoa ja huoltoa ja parantaa merkittävästi vakaiden koodielementtien uudelleenkäytön tehokkuutta rajapintojen kautta.
LSP: Liskovin korvattavuusperiaate
Barbara H. Liskovin ja Jeannette M. Wingin teoksessa ”Behavioral Subtyping Using Invariants and Constraints” LSP:n mukaan:
“Olkoon q(x) tyypin T objektien x suhteen todistettavissa oleva ominaisuus. Silloin q(y) pitäisi olla todistettavissa tyypin S objektien y suhteen, jossa S on T:n alityyppi”.
Se saattaa kuulostaa salaperäiseltä, mutta se on itse asiassa melko helppo ymmärtää: linkitetyt tai laajennetut alaluokat doivent toimia kuten niiden yläluokat tai perusluokat. Tämä tarkoittaa, että jokaisen alaluokan on säilytettävä perintönä yläluokan ominaisuudet, eikä näitä ominaisuuksia saa muuttaa alaluokassa. Ne doivent olla periaatteessa korvattavissa, mistä johtuu korvattavuusperiaate. Yläluokkia sen sijaan voidaan muokata.
Selventääksemme asiaa, tarkastellaan Robert C. Martinin klassista esimerkkiä suorakulmioista ja neliöistä. Geometrian oppitunneilla opimme seuraavan periaatteen: jokainen neliö on suorakulmio, mutta kaikki suorakulmiot eivät ole neliöitä. Neliöllä on suorakulmioiden tavoin suorat sivut, mutta lisäksi sen kaikki sivut ovat yhtä pitkiä.
Ohjelmoinnissa oletetaan, että samankaltaiset tai näennäisesti identtiset luokat ovat toisiinsa liittyviä tai toisistaan riippuvaisia, mikä johtaa virheisiin, väärinkäsityksiin ja epäselvään koodiin. Tästä syystä ohjelmoinnissa luokka “Rectangle” ei ole neliö, eikä luokka “Square” ole suorakulmio. Molemmat ovat irrotettuja toisistaan ja toteutettu erikseen. Ilman integroitua yhteyttä luokkien välillä väärinkäsitykset eivät voi johtaa luokkien välisiin virheisiin. Tämä parantaa turvallisuutta ja vakautta, kun alaluokkien tai yläluokkien toteutuksia vaihdetaan ilman seurauksia.
ISP: rajapinnan erottamisen periaate
Robert C. Martinin teoksessa ”The Interface Segregation Principle” ISP määritellään seuraavasti:
”Asiakkaita ei pitäisi pakottaa käyttämään rajapintoja, joita he eivät käytä”.
ISP toteaa, että käyttäjien ei pitäisi joutua käyttämään rajapintoja, joita he eivät tarvitse. Toisin sanoen: Jotta asiakkaille voidaan tarjota tiettyjen luokkien toiminnot, uusia, pienempiä rajapintoja räätälöidään erityisvaatimuksiin. Tämä estää rajapintojen kasvamisen liian suuriksi ja varmistaa, että luokkien välille ei muodostu vahvoja riippuvuussuhteita. Etuna on, että ohjelmisto, jossa on irrotetut luokat ja useita pieniä, erityisvaatimuksiin räätälöityjä rajapintoja, on helpompi ylläpitää.
DIP: Riippuvuuden kääntämisen periaate
Robert C. Martinin teoksessa ”The Dependency Inversion Principle” (Riippuvuuden kääntämisen periaate) viides ja viimeinen SOLID-periaate on seuraava:
”A. Korkean tason moduulit eivät saisi olla riippuvaisia matalan tason moduuleista. Molempien tulisi olla riippuvaisia abstraktioista. B. Abstraktiot eivät saisi olla riippuvaisia yksityiskohdista”.
DIP varmistaa, että lähdekoodikerrosten tietyt toiminnot ja riippuvuudet perustuvat abstrakteihin rajapintoihin, eivät suoraan toisiinsa. Ohjelmistoarkkitehtuurit on tyypillisesti järjestetty korkeampiin käyttäjätasoihin ja alempiin, abstraktimpiin tasoihin. Loogisesti ajateltuna voisi kuvitella, että abstrakti perusta vaikuttaa ylempien tasojen toimintaan. DIP kuitenkin tunnistaa tässä potentiaalisen ongelman, koska se luo riippuvuuksia ylemmille tasoille alemmista tasoista, mikä voi johtaa ongelmiin.
Sen sijaan, että korkeammat tasot linkitetään alempiin tasoihin, korkeiden ja matalien tasojen luokat tulisi perustaa abstrakteihin, välittäviin rajapintoihin. Rajapinnat hakevat alemmilta tasoilta korkeammilla tasoilla tarvittavat toiminnot ja asettavat ne saataville. Tällä tavoin voidaan välttää alhaalta ylöspäin suuntautuva riippuvuushierarkia, joka voi ajan mittaan johtaa virheisiin koodissa. Tämä helpottaa moduulien uudelleenkäytettävyyttä ja mahdollistaa alempien luokkien muutokset vaikuttamatta korkeampiin tasoihin.
Mitä tapahtuu, jos SOLID-periaatteita ei noudateta?
Puhtaan, luettavan ja ylläpitoa helpottavan koodin luominen tulisi olla ohjelmistokehityksen ensisijainen tavoite. Jos kehittäjät laiminlyövät SOLID-periaatteiden kaltaisia olennaisia ohjeita, koodi voi heikentyä huomattavasti haavoittuvuuksien, päällekkäisyyksien, kertyneiden virheiden ja liiallisten riippuvuuksien vuoksi. Äärimmäisissä tapauksissa koodi voi ajan myötä tulla käyttökelvottomaksi. Tämä on merkittävä ongelma ketterässä ohjelmistokehityksessä, jossa monet ihmiset työskentelevät usein monimutkaisten koodaustöiden parissa.
Epäpuhtaan koodin tai huonon koodin ylläpidon seuraukset ovat muun muassa:
- Koodin haju: Kun koodia ei ole kirjoitettu tarvittavien standardien mukaisesti, se voi aiheuttaa koodin hajua tai “haisevaa koodia”, mikä johtaa toiminnallisiin virheisiin ja yhteensopimattomiin ohjelmiin.
- Koodin rappeutuminen: Jos koodia ei ylläpidetä tai korjata refaktorisoimalla tai kalliilla koodin tarkistuksella, se voi kuvaannollisesti “rappeutua” ja menettää täysin toimintakykynsä. Toinen termi lukukelvottomalle, monimutkaiselle koodille on spagettikoodi.
- Turvallisuusriskit: Ongelmat eivät rajoitu vain käyttökatkoihin, monimutkaiseen ylläpitoon ja yhteensopivuusongelmiin. On myös turvallisuusaukkoja, jotka antavat haittaohjelmille mahdollisuuden hyödyntää koodia, mukaan lukien nollapäivähyökkäykset.
Kuka kehitti SOLID-periaatteet?
SOLID-periaatteiden alkuperä juontaa juurensa useista periaatteista, jotka Robert C. Martin (”Uncle Bob”), yksi ketterän ohjelmoinnin aloitteentekijöistä, esitteli ensimmäisen kerran vuonna 2000 esseessään ”Design Principles and Design Patterns” (Suunnitteluperiaatteet ja suunnittelumallit). SOLID-periaatteet ovat Robert C. Martinin, Bertrand Meyerin ja Barbara Liskovin keksimiä. Tarttuvan akronyymin popularisoi Michael Feathers, joka järjesti viiden keskeisen periaatteen alkukirjaimet mieleenpainuvaan järjestykseen.
Mitä vastaavia ohjelmointiperiaatteita on olemassa?
Ohjelmistokehityksessä periaatteet ovat yleisiä tai hyvin tarkkoja ohjeita ja suosituksia toiminnalle. Objektisuuntautuneeseen ohjelmointiin kehitettyjen SOLID-periaatteiden lisäksi muita puhtaan koodin ohjelmointiperiaatteita ovat:
- DRY-periaate (Don’t repeat yourself) toiminnoille, joilla on yksi ainoa esitysmuoto
- KISS-periaate (Keep it simple, stupid) mahdollisimman yksinkertaisesti rakennetulle koodille