Ensimmäinen projekti

Ohjelmointityyli


C++:lla voi kirjoittaa hyvinkin kryptistä koodia, mutta turhaa kryptisyyttä kannattaa välttää. Tavoitteena on kirjoittaa selkeää koodia, jota on helppo lukea, ymmärtää ja ylläpitää.

Ohjelman laatuun vaikuttavat esimerkiksi selkeys, luettavuus, ymmärrettävyys, ylläpidettävyys, toimivuus ja tehokkuus. Tyylikkään koodin kirjoittamiseen ei ole yhtä ainoaa oikeaa tapaa. Tällä kurssilla käytetään seuraavia tyylisääntöjä, joita sovelletaan myös projektien arvostelussa.

Toimivuus ja käyttäjän huomioiminen

Ohjelman toimivuus on olennainen laatuvaatimus. Hyvä rakenne ja yksinkertaiset ratkaisut helpottavat myös oikean toiminnan saavuttamista. Ohjelman pitäisi:

  • toimia oikein kaikilla sallituilla syötteillä
  • ilmoittaa virheellisistä syötearvoista ja varoittaa virheellisestä käyttötavasta
  • tarkistaa syötteet riittävän kattavasti, jotta virheellinen syöte ei kaada ohjelmaa hallitsemattomasti
  • laskea itse tiedot, jotka se voi päätellä, sen sijaan että kysyisi ne käyttäjältä
  • olla käyttäjälle mahdollisimman helppo ja havainnollinen.

Jaa ohjelma selkeisiin osiin

Strukturointi tarkoittaa ohjelman jakamista riittävän pieniin, yksinkertaisiin ja hallittaviin osiin. Se koskee esimerkiksi funktioita ja luokkia.

Funktio ei saisi olla liian pitkä tai leveä: sen pitäisi mahtua näytölle kokonaan ja sen tarkoituksen pitäisi olla helppo hahmottaa. Toisaalta yhden rivin funktio tai funktio, joka vain kutsuu toista funktiota, on yleensä liian lyhyt. Poikkeuksena ovat esimerkiksi getter-metodit, jotka palauttavat luokan attribuutin arvon. Lyhyt funktio voi olla perusteltu myös silloin, kun se antaa monimutkaiselle ehdolle selkeän nimen:

bool isSnakeDead() {
    return newHead.x < 0 || newHead.x >= width_ ||
           newHead.y < 0 || newHead.y >= height_;
}

Vältä myös toisteista koodia eli saman tai lähes saman toteutuksen kirjoittamista useaan kertaan. Toisteisuutta voi usein vähentää tekemällä yhteisen funktion tai parametrisoimalla toimintaa.

Nimeä asiat kuvaavasti ja johdonmukaisesti

C++ on merkkikokoriippuvainen: esimerkiksi nimi ja Nimi ovat eri tunnisteita. Niiden käyttäminen rinnakkain tekee koodista kuitenkin helposti sekoitettavaa. Vältä siksi myös keskenään liian samankaltaisia nimiä.

Muuttujien, vakioiden, funktioiden, tyyppien ja luokkien nimien tulee kertoa, mitä ne tarkoittavat. Nimi saa olla pitkä, jos se tekee tarkoituksen selväksi. Yhden merkin nimeä, kuten i, voi käyttää silloin, kun muuttuja on lyhytikäinen ja sen rooli on ilmeinen, esimerkiksi indeksinä:

for (vector<int>::size_type i = 0;
     i < lukuvektori.size();
     ++i) {
    cout << lukuvektori.at(i) << endl;
}

Tyypillinen nimeämistapa on aloittaa muuttujien ja funktioiden nimet pienellä kirjaimella ja tyyppien, luokkien sekä tietueiden (struct) nimet isolla kirjaimella. Vakiot kirjoitetaan usein kokonaan isoilla kirjaimilla ja sanat erotetaan alaviivalla.

Funktioiden nimissä voi käyttää esimerkiksi muotoa printField tai print_field. Ensimmäinen on tällä kurssilla suositeltu tapa. Tärkeintä on käyttää samassa ohjelmassa johdonmukaista tyyliä. Nimeä funktio toiminnalla, kuten print tai tulosta, älä toiminnan substantiivilla, kuten printing tai tulostaminen.

Totuusarvoisen funktion nimen kannattaa alkaa esimerkiksi is- tai onko-sanalla, jotta nimi kertoo, milloin funktio palauttaa true. Esimerkiksi isReady kertoo tarkoituksen paremmin kuin checkStatus. Jos funktion päätarkoitus on tehdä jokin toiminto ja totuusarvo kertoo vain, onnistuiko se, nimeäminen voi kuvata ensisijaista toimintoa: esimerkiksi setValue voi palauttaa true onnistuessaan ja false epäonnistuessaan.

Käytä nimettyjä vakioita ja hyödyllisiä kommentteja

Käytä nimettyjä vakioita taikanumeroiden sijaan. Esimerkiksi nimi NUMBER_OF_STUDENTS kertoo paremmin luvun 600 merkityksen. Jos arvo muuttuu, se tarvitsee päivittää vain yhteen paikkaan. Nimeä vakio sen käyttötarkoituksen mukaan; esimerkiksi HUNDRED ei kerro, mihin sataa käytetään.

Kommentit voivat auttaa ymmärtämään koodia, mutta ne eivät saisi vain toistaa sitä, minkä koodi jo kertoo. Vertaa:

// Jos varauslippu on nolla
if (AllocFlag == 0) { ... }

Kommentti ei selitä tässä koodia enempää. Jos nolla tarkoittaa uuden jäsenen varaamista, ilmaise merkitys nimetyllä vakiolla:

// Varataan uusi jäsen
if (AllocFlag == NEW_MEMBER) { ... }

Nyt koodi kertoo tarkoituksen, joten kommentin voi jättää pois. Kommentoi erityisesti vaikeasti ymmärrettävät vaiheet, funktiot ja tiedostot. Tiedoston alussa tulee olla tiedostoa koskeva kommentti ja pääohjelmatiedostossa koko ohjelmaa koskeva kommentti. Funktion kommentista tulee ilmetä, mitä funktio tekee, mitä sen parametrit tarkoittavat ja mitä paluuarvo kertoo. Jos käytössä on otsikkotiedosto (.hh), kirjoita funktion kommentti sinne esittelyn yläpuolelle.

Muotoile koodi luettavaksi

C++ ei vaadi sisennyksiä, mutta niitä tulee käyttää koodin rakenteen selventämiseksi. Käytä aaltosulkeita ({ }) aina kontrollirakenteissa, vaikka rungossa olisi vain yksi lause. Sulkeet voi kirjoittaa omille riveilleen tai avaus-sulkeen voi sijoittaa ehtorivin loppuun. Valitse tapa ja käytä sitä johdonmukaisesti koko ohjelmassa.

Kurssilla noudatettavia lisäsääntöjä

  • Pidä muuttujan näkyvyysalue mahdollisimman pienenä, esimerkiksi lohkossa, funktiossa tai luokassa.
  • Määrittele kukin muuttuja omassa lauseessaan. Kirjoita int a; ja int b; sen sijaan, että määrittelisit molemmat muodossa int a, b;.
  • Alusta jokainen muuttuja ennen sen käyttöä.
  • Kirjoita osoittimen ja viitteen merkit heti tyypin nimen perään, esimerkiksi int* ptr = nullptr; ja const vector<int>& vec.
  • main-funktion paluuarvon tyyppi on aina int.
  • Anna parametrien nimet sekä funktion esittelyssä että määrittelyssä ja pidä nimet samoina.
  • Välitä säiliöparametri viitteenä tai vakioviitteenä aina, kun se on tarkoituksenmukaista. Käytä vakioviitettä, jos funktio ei muuta säiliötä.
  • Kirjoita oletusarvoiset parametrit funktion esittelyyn, älä uudelleen määrittelyyn.
  • Älä palauta osoitinta tai viitettä funktion omaan paikalliseen dataan.
  • struct-tietueessa ei käytetä jäsenfunktioita; rakentajat ja vertailuoperaattorit ovat sallittuja.
  • Käytä vektorin ja muiden säiliöiden alkioihin pääsemiseen tarkistavaa at-metodia hakasulkujen sijaan.
  • Säiliön alkioiden tulee olla samanlaisia myös merkitykseltään. Jos tallennat esimerkiksi henkilön etunimen, sukunimen ja osoitteen, käytä niille kuvaavaa tietuetta yhden merkkijonovektorin sijaan.

Vältä turhaa monimutkaisuutta

Palauta totuusarvoinen ehto suoraan sen sijaan, että muuttaisit sen ensin if-rakenteella arvoksi true tai false. Esimerkiksi:

bool isNegative(int number) {
    return number < 0;
}

Jos funktio tekee vain tämän yhden vertailun, pohdi myös, onko erillinen funktio tarpeen.

C++:n poikkeusmekanismia ei käsitellä tällä kurssilla. Jos et varmasti osaa käyttää poikkeuksia, älä yritä käyttää niitä. Älä käytä poikkeuksia kontrollirakenteiden korvikkeena, esimerkiksi Pythonista tutun virheenkäsittelyn tapaan.

Lopuksi

C++:ssa on myös luokkiin ja olio-ohjelmointiin liittyviä tyylisääntöjä. Niihin palataan näitä aiheita käsiteltäessä. Kurssin koodipohjissa ja esimerkeissä pyritään noudattamaan yllä olevia sääntöjä, mutta kaikki esimerkit eivät välttämättä ole täydellisiä. Ilmoita havaitsemistasi tyylisääntöjen rikkomuksista.

Muista: tyylisäännöt auttavat tekemään ohjelmasta luettavan ja helpommin ylläpidettävän. Johdonmukaisuus ja koodin selkeä tarkoitus ovat tärkeämpiä kuin yksittäinen nimeämistapa.