Koodimisstiil

Sissejuhatus

Koodimisstiil on dokument, mille eesmärk on kirjeldada missugune peaks kirjutatud programmikood välja nägema. Stiili jälgimise peamine eesmärk on parandada koodi loetavust ja tagada ühtlane stiilikasutus üle kogu programmi. Parem loetavus täidab ka mitmeid varjatud eesmärke – näiteks on koodist vigu oluliselt lihtsam leida, seda nii sul endal kui ka sind abistavatel või sinuga koos töötavatel inimestel.

Koodimisstiilid erinevad ja seda mitmeti. Ühes programmeerimiskeeles võib sageli esineda mitu konkureerivat koodimisstiili. Samuti erinevad koodimisstiili juhised ühest programmeerimiskeelest teise. Lisaks erinevad ka koodimisstiili nõuded ettevõttest ettevõttesse. Eriti oluline on viimane – kui sinu tööandjal on kindel koodimisstiili nõue, mida kõik töötajad aktsepteerivad ja kasutavad, siis tuleb sellest ka kinni pidada, hoolimata personaalsest eelistusest (või läheneda argumenteeritult miks seda tuleks muuta).

Selles õppeaines on samuti määratletud koodimisstiil, mida tuleb jälgida ja mis on siin kirjeldatud. Oleme koodimisstiili koostamisel lähtunud võimalikult algajasõbralikust lähenemisest, mis peaks tagama maksimaalset koodi loetavust.

Esmalt kodeerima hakates võivad sellised nõuded tunduda küsitava väärtusega kui isegi töötavat programmi on keeruline kirjutada, kuid see ei tähenda, et stiili ja funktsionaalsust tuleks eraldi käsitleda. Need käivad käsikäes ja toetavad üksteist. Alustades stiilist kinni pidamist kohe esimesest nädalast kujuneb see sul nö musklimällu ja mõne aja pärast ei pea sa enam sellele teadlikult mõtlema ega ümber õppima.

Märkus: Kui sul on juba välja kujunenud koodimisstiil, mis on laialdaselt aktsepteeritud ja mis ei lange täielikult kokku siin kirjeldatuga (näiteks ettevõttes kus töötad), võib seda kasutada. Sellegipoolest tuleb jälgida parimaid praktikaid, isegi kui varasemalt pole kõiki neist rakendatud. Näiteks võivad erineda sinu muutujate kirjutamise stiil (snake_case) või loogeliste sulgude asukoht (rea lõpus, mitte eraldi real).

Koodirea pikkus

Klassikaliselt on soovitav koodirea pikkus 80 tähemärki. See tuleb ajalooliselt monitoride võimekusest kuvada 80 tähemärki rea kohta.

Kuna tehnika on vahepeal oluliselt arenenud, siis Programmeerimine 1 ja 2 kursustes on tegu rangelt soovitatava pikkusega (tulevane tööandja nii leebe ei pruugi olla). St kui lähed mõne tähemärgi üle, siis sellest veel numbrit ei tehta. Küll aga 90 ja enama tähemärgi korral tuleb  juba kas ridu poolitada või koodi ümber struktureerida!

Koodirea pikkuse jälgimiseks veendu, et su koodiredaktor kuvaks sulle rea pikkuse kohta indikaatori.

Näiteks Geany puhul: Edit -> Preferences -> Editor -> Display
Vali Long line marker: Enabled ja column: 80

Koodirea poolitamine

C keel ei hooli üldiselt tühikutest ja tabidest, seega poolitada ridu saame ilma ühtegi lisasümbolit kasutamata. Ridade poolitamine on osaliselt reguleeritud aga osaliselt loominguline, et tulemus jääks loetav.

  • Rida poolita enne 80 tähemärgi täitumist.
  • Poolitatud read peavad olema trepitud vähemalt ühe võrra.
  • Ridade poolitamine on osaliselt ka loominguline tegevus. Olenevalt poolitamise iseloomust võib loetavuse huvides olla mõistlik ka rohkem treppida.
  • Ridu tasub poolitada operaatorite ees või järel.
  • Järgnevad koodiread  jätkuvad esialgselt taandelt.
Ära nii tee:

Järgnev koodirida on nii pikk, et vajab horisontaalset kerimist ja on raskesti jälgitav.

Sõnede poolitamine

Sõnede poolitamisel kehtivad täiendavad reeglid. Nimelt ei tohi sõne poolitada jutumärkide vahelt lihtsalt rida vahetades.

  • Sõne poolitamiseks lõpeta esmalt jutumärgid ära ja seejärel alusta jutumärgid uuesti järgmisel real.
  • Poolitatud sõne tuleks alustada samakaugelt kui eelneval real jutumärgid algasid.
  • Alternatiivina võib tükeldada prinditava teksti mitme funktsioonikutse vahel ära.

Koodi kommenteerimine

Koodi kommenteerimise eesmärk on suurendada programmikoodi loetavust ja hallatavust. Programmi kirjutamise hetkel võib tunduda kõik äärmiselt lihtne ja arusaadav kuna see on sul parasjagu mõttes. Paraku tuleme me enamasti programmide juurde tagasi mõne päeva, nädala, kuu või miks mitte ka aastate pärast ning siis on juba väga keeruline kommenteerimata koodi lahti mõtestada. Veel hullem, vigu leida, parandada ja muudatusi sisse viia.

Hästi kommenteeritud kood pole mitte oluline vaid sinule, vaid tulevikus ka su kolleegidele, kes sinuga koos töötama peavad ja sama koodibaasi haldavad.

Kommenteerida tuleks eelkõige “huvitavaid” koodilõike. Arusaam mis on huvitav ja mis mitte tekib kogemusega. Kindlasti ei ole mõtet igat koodirida kommenteerida – see teeb koodi loetamatuks. Kommentaarid võiksid olla iga mõne koodirea järel. Alustades võiksid sihiks võtta, et keskmiselt iga 5 koodirea kohta on vähemalt 1 kommentaar.  Kogemuste tekkides näed, et vahel on vaja igale reale kommentaari, vahel aga näed, et 10-20 rea kohta pole ühtegi vaja.

Koodi kommenteerimiseks on mitmeid võimalusi:

  • Eraldi real olev kommentaar
  • Samal real olev kommentaar
  • Mitmerealised kommentaarid (vt Faili päis)
  • Soovitav on kommentaar paigutada eelnevale reale iseseisvalt – sedapidi saavutad kõige parema loetavuse.
  • Kommentaari algustähise ja sisu vahel on tühik.
  • Kommentaar algab kahe kaldkriipsuga ja lõppeb rea lõppedes.
  • Eelista lühikesi ja konkreetseid kommentaare pikkadele ja lohisevatele.
  • Pea kinni rea maksimaalsest pikkusest.

Milline on hea ja milline halb kommentaar

Hea kommentaar selgitab

  • mida sa teed
  • miks sa seda teed

Kusjuures vastama ei pea mõlemale küsimusele korraga.

Ära nii tee! Halb kommentaar selgitab kuidas sa seda teed.

Halvaks teeb sellise kommentaari see, et esiteks ei anna see mitte midagi uut – sa kordad juba seda mis on koodis kirjas. Teiseks, kui koodis on viga, siis kommentaar ei selgita mida sa tegelikult teha soovisid – näiteks andes probleemse koodi sõbrale lugemiseks pole võimalik tal leida ebakõla kommentaari ja koodis kirjutatu vahel..

Faili päis

Kõikide .c ja .h failide päisesse tuleks lisada koodifaili ja autori(te) nimed ning lühike selgitus mida fail sisaldab. Avalike projektide puhul lisatakse reeglina ka autori veebileht ja/või e-postiaadress.

Sageli lisatakse failidesse ka loomise ja viimase muutmise aeg või faili versiooninumber. Osadel juhtudel hoitakse faili päises ka muudatuste ajalugu, kuid viimasel ajal paikneb see pigem versioonihalduses (Nt Git).

Faili päise puhul kasutatakse kommenteerimise viisi, kus kommentaar algab märkidega /* ja lõpeb märkidega */. Kõik vahepealne loetakse kommentaariks.

Muutujate deklareerimine ja initsialiseerimine

  • Muutujate nimed peaksid kirjeldama nende sisu
  • Nimetamisel kasutame kaameliküüru ( lowerCamelCase ) meetodit – st ühesõnalised muutujad algavad väikese tähega ja mitmesõnaliste puhul on järgnevad sõnad suure algustähega.
  • Ühest tüübist võib deklareerida mitut muutujat, eraldades need koma ja tühikuga, kuid sellisel juhul ei tohi neid algväärtustada samal real.
  • Globaalmuutujate kasutamist tuleb vältida (välja arvatud mõned erijuhud).
  • Muutujaid võid deklareerida nii funktsiooni alguses (C90 standard) või seal kus kasutusele võtad (C99 standard). Viimase puhul arvesta, et osad kompilaatorid ei pruugi seda toetada.
Ära nii tee!
  • Esimese rea puhul on väga keeruline aru saada mis muutuja mis eesmärki täidab.  Ühetähelisi muutujaid tuleks vältida enamikes kohtades. Üldjuhul on ühetähelised muutujad mõistlikud vaid tsükliloenduritena või üldmõistetavas tähenduses (nt n – objektide hulk).
  • Teise näite puhul on segatud algväärtustatud ja algväärtustamata muutujad. Kõik algväärtustatud muutujad deklareeri eraldi ridadel, sedasi vähendad tõenäosust tähelepanematusest vigu teha.
  • Viimase näite puhul võib olla tegu küll põhjaliku kirjeldusega, kuid on loetamatu, kuna sõnade eraldust pole näha (kaameliküüru meetod!). Samuti tasub kaaluda kui pikk on mõistlik muutuja nimetus.

Viitmuutujate deklareerimine (pointer)

Viitade puhul on olulised samad põhireeglid nagu tavaliste muutujate puhul.  Küll aga lisanduvad mõned reeglid.

  • Sümbol * paikneb muutuja nime ees
  • Tavaliselt pannakse viitmuutuja ette väike p täht, näitamaks, et tegu on viidaga.
  • Viitmuutujad deklareeritakse eraldi tavamuutujatest.
Ära nii tee!

  • Pannes tärni andmetüübi järele tekitab see petliku mulje, et kõik antud real deklareeritavad muutujad on viidad.
  • Teise rea puhul võib tekkida petlik mulje, et deklareeritakse 2 viitmuutujat. Tegelikult on viit vaid pList. val on tavaline int  tüüp muutuja. Deklareeri alati viidad eraldi tavalistest muutujatest!

Arvutused ja loogikaavaldised

  • Kõik toimingud, mis kasutavad vähemalt 2 operandi tuleb eraldada tühikutega
  • Unaarsed operaatorid kirjutatakse kokku operandiga. Näiteks  !holiday  ehk NOT holiday, ~a  ehk INV a (bitikaupa inverteerimine), k--  (dekrementeerimine 1 võrra)
Ära nii tee!

Tühikute puudumise tõttu on väga raske jälgida tehte korrektset kirjapanekut või seda muuta.

#define makrod

  • Nimetus kirjutatakse suurtähtedes
  • Üks kasutusvaldkondadest on maagiliste numbrite vältimine koodis
  • Mitmesõnalised nimed kirjutatakse altkriipsuga
Ära nii tee!
  • Esimesel juhul võib tekkida hilisemalt segadus, et limit väärtust on võimalik muuta, kuna nimetamisstiil on sama nagu deklareeritud muutujatel.
  • Teisel on taaskord loetavus kehv kuna sõnade eraldajaid pole näha, olgugi, et kirjeldus on piisav.

Tingimuslaused

  • Loogelised sulud algavad ja lõppevad eraldi real.
  • Loogelised sulud on samal kaugusel vasakust äärest – st joonduvad kenasti üksteise all.
  • Loogeliste sulgude sisu on 1 võrra edasi trepitud (4 tühikut)
  • Märksõna else  asub eraldi real. else  ja eelneva lõppeva loogelise vahel tühja rida ei ole!
  • Märksõna if  järel on 1 tühik.
  • Aritmeetiliste ja loogikaoperaatorite (=, +, ==, <=, <, >, ||, &&  jne) ees ja järel on üks tühik. See ei laiene unaarsete operaatoritele (~, ^, &. |, !)
Ära nii tee!
  • Loogeliste paigutamine tekitab segadust olenemata järgitavast koodimisstiilist.
  • Tühi rida enne else  märksõna tekitab olukorra kus seotus eelneva if  lausega pole koheselt arusaadav. Samuti tekitab kiusatust sinna vahele mõne koodirea kirjutamiseks, mis oleks juba viga.

Switch lause

  • Märksõna switch järel on 1 tühik
  • Märksõna case ja selle väärtuse vahel on 1 tühik
  • Switch lause sisu on loogeliste vahel. Loogelised on eraldi ridadel ja horisontaalselt joondus (sama nagu if-lause)
  • switchi sisu on trepitud 1 võrra
  • case sisu on trepitud ühe võrra

Treppimine

Treppimine (indentation) on tegevus, mille käigus me muudame koodi taanet (kaugus vasakust äärest). Koodi treppimine on oluline loetavuse parendamiseks. Trepitud koodi puhul on hea jälgida millised koodiread on millise koodiploki sees.

Treppimise astmeks meie antavates kursustes on valitud 4 tühikut. Kui kasutad treppimiseks tabulaatori (tab) klahvi on soovitav oma koodiredaktoris see seadistada asenduma 4 tühikuga. Vastasel juhul võib koode näha välja loetamatu avades mõne teise redaktoriga (erinevad koodiredaktorid võivad näidata tabi näiteks 2, 4 või ka 8 tühikuga).

Väldi tabi ja tühiku ristkasutust!

 

Järgnevalt on esitatud üks viisakalt trepitud koodi kondikava.

Märka järgnevat:

  • Koodiplokkide loogelised sulud asetsevad alati üksteise all. Sedasi on lihtne aru saada mis laused on koodiploki sees.
  • Algavad ja lõppevad loogelised sulud on eraldi ridadel.
  • Loogeliste sulgude vahele jääv sisu trepitakse ühe võrra (4 tühikut)
  • Vihje: enamikes koodiredaktorites saad taanet suurendada valitud (1 või rohkem) koodiridadel kasutades tab klahvi. Taande vähendamiseks valitud ridadel saab kasutada shift+tab klahvikombinatsiooni.
  • Soovitus: Seadista oma koodiredaktor sedasi, et algava sulu järel tekitatakse automaatselt ka lõppev sulg. Kui seda võimalust pole, tee seda ise! Vastasel juhul võib ununeda märgis. Geany seadistuse soovituses oleme just nii teinud.
  • Soovitus:  lase oma koodiredaktoril ise treppida. Enamik koodiredaktoreid lisab koodi taanet automaatselt kui oled pärast algavat loogelist sulgu vajutanud enter klahvi. Sedasi väldid vigu, peavalu ja asjatut manuaalset tööd.

Ära nii tee:

Järgneva koodi puhul on väga keeruline aru saada mis laused mille sees asuvad

Treppimise sügavus

Väldi enama kui 3 koodiploki üksteise sisse paigutamist. Sama piirang kehtib enamikes ettevõtetes (osades lubatakse ka 4).

Liigne koodiplokkide üksteise sisse pesastamine muudab koodi raskesti jälgitavaks. Lahenduseks on koodi ümber organiseerimine (refaktoriseerimine). Näiteks võib osa koodist paigutada funktsiooni, pesastatud tingimuslaused saab kokku kirjutada ning osa koodis olevatest lausetes võib olla võimalik ümber paigutada.

Tsüklid

  • Märksõnade while  ja for  järel on 1 tühik
  • Loogelised sulud on samal kaugusel (horisontaalselt joondus), eraldiseisvatel ridadel
  • Loogeliste sulgude vahele jääva koodiploki read on trepitud 1 võrra.
  • Märka kindlasti ka for  lauses semikoolonite järel ja operaatorite ees ja järel olevaid tühikuid.

Pesastatud for tsüklid

Tsükleid üksteise sisse paigutades tuleb jälgida treppimist. Iga järgnev koodiplokk viib taanet 1 võrra edasi.

 Funktsioonid

 main() funktisoon

või

  • Esimest kirjapilti kasutatakse olukorras, kus programmile ei soovita edastada parameetreid käsurealt.
  • Teist kirjapilti kasuta ainult siis kui soovid käsurealt parameetreid edastada (nt faili nimetus)

 Enda loodud funktsioonid

  • Funktsiooni nimest peab olema võimalik välja lugeda mida see teeb.
  • Funktsioonide nimetamisel kasutame suure algustähega kaamelüküüru stiili ( UpperCamelCase ), kus kõik sõnad kirjutatakse kokku ning algava suure tähega. Sedasi saame eristada neid kenasti muutujatest, makrotest jt.
  • Enda loodud funktsioonidel peab olema prototüüp, mis paikneb kas samas failis enne main() funktsiooni või eraldi päisefailis.

Funktsiooni kommentaarid

Funktsioonile peab alati eelnema kommentaar, mis annab selge arusaama mida funktsioon teeb (detailsemalt). Olenevalt funktsiooni keerukusest võib kasutada kas standartset või lihtsustatud kuju.

Standartne funktsiooni kommentaari kuju
  • Kirjeldus – sisaldab funktsiooni eesmärki, tööpõhimõtteid, piiranguid. Kui funktsiooni käigus muudetakse andmeid, mis pole funktsioonile lokaalsed, siis see tuleb samuti kirja panna (nt massiivid)
  • Funktsiooni parameetrid – nende nimed, andmetüübid ja väärtuse tähenduslikkus antud funktsioonis. Kui eksisteerivad, siis ka piirangud (eeltingimused) väärtustele
  • Funktsiooni poolt tagastatav väärtus – andmetüüp ja selle tähenduslikkus
Lihtne kommentaari kuju

Antud kommentaari kuju tohib kasutada vaid väga lihtsate funktsioonide puhul.

Funktsiooni prototüüp

Enda loodud funktsioonide puhul tuleb kirjeldada funktsiooni prototüüp. Funktsiooni prototüübid paiknevad vahetult enne main()  funktsiooni või eraldi päisefailis.

  • Prototüüp kirjeldab ära funktsiooni tagastuse, nime ja parameetrid (andmetüüp ja nimi).
  • Funktsiooni prototüüp on sisuliselt koopia funktsiooni päisest selle deklaratsiooni juures.

Loendid ehk enumeration

Loendite nimetamise stiiliks kasuta  UpperCamelCase  loendi tunnuste nimetamiseks SCREAMING_SNAKE_CASE .

Kasutada võib üherealist definitsiooni

või mitmerealist definitsiooni

NB! Väldi loendite tüübi ümberdefineerimist ( typedef ) kuna see peidab informatsiooni.

Kirjed ehk struktuurid (struct)

Struktuuride puhul kasutame samu treppimise ja loogeliste sulgude paigutamise reegleid nagu varasemalt.

Märkus! Kuigi tüüpide ümberdefineerimist käsitletakse sageli kui informatsiooni peitmisena on selle kursuse raames struktuuride ja  ühendite (union) ümbernimetamine lubatud.