Helsingin yliopisto / Tietojenkäsittelytieteen laitos / 581258-1 Johdatus ohjelmointiin
Copyright © 1999 Arto Wikla. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

Pieni tyyliohje aloittelevalle Java-ohjelmoijalle

(Muutettu viimeksi 27.1.1999)

Tämä sivu esittelee joitakin tapoja ulkoasultaan selkeiden Java-ohjelmien kirjoittamiseen.

Inspiraation lähteinä olen käyttänyt omaa kurssimateriaaliani, Sun-yhtion alkuperäisten Javan API-kuvausten ja muunkin dokumentaation tyyliä sekä Jari Juslinin erinomaista tyyliopasta.

Tätä ohjetta ei ole tarkoitettu kattavaksi tai sitovaksi. Ennemminkin kyseessä on esimerkki, jonka avulla voi rakennella oman selkeän tyylinsä. Joistakin kohdista voi olla perustellusti eri mieltä. Tavoitteesta ei. Se on se selkeä tyyli.

Sisältö:

Miksi ohjelmien pitää olla selkeitä

"Oikeat ohjelmat" ovat yleensä suuria ja useimpia ylläpidetään: virheitä korjataan, toimintoja muutellaan vaatimusten muuttuessa. Useat ohjelmoijat saattavat työskennellä ohjelman parissa sen elinaikana.

Tuhatrivinen ohjelma on vielä aika pieni, mutta jo sellaisen hahmottaminen on mahdotonta, jos ohjelmoija ei ole kirjoittanut ohjelmaansa selkeäksi. Jopa muutaman rivin mittainen "mikroskooppisen pieni" ohjelma on hyvin vaikeasti ymmärrettävissä, ellei ymmärtämistä auteta ulkoasun selkeydellä.

Mitä seuraava ohjelma tekee?

public class ABC{public static 
void main(String[] args){double x,y;System.out.println
("Anna kaksi lukua!");x 
= Lue.dluku();y = Lue.dluku();if (x 
> y)System.out.println("Ensimmäinen luku on suurempi."
);else if (x < y)System.out.println(
"Toinen luku on suurempi.");else 
System.out.println("Luvut ovat yhtä suuret.");}}

Vastaus löytyy paljon helpommin, kun ohjelman ulkoasu auttaa ohjelman rakenteen hahmottamisessa:
public class KumpiSuurempi { /* ilmoittaa kumpi kahdesta syöttöluvusta 
                                 on suurempi                           */

  public static void main(String[] args) {
    double luku1, luku2;

    System.out.println("Anna kaksi lukua!");
    luku1 = Lue.dluku();
    luku2 = Lue.dluku();

    if (luku1 > luku2)
      System.out.println("Ensimmäinen luku on suurempi.");
    else if (luku1 < luku2)
      System.out.println("Toinen luku on suurempi.");
    else
      System.out.println("Luvut ovat yhtä suuret.");
   }
}

Molemmat ohjelmat ovat Java-kääntäjälle kuitenkin ihan "yhtä hyviä"!

Kommentoinnista

Jokaisen Java-ohjelmatiedoston eli ns käännösyksikön alkuun on syytä kirjoittaa selostus, mistä ohjelmasta tai ohjelman osasta on kyse, kuka on tekijä, koska ohjelmaa on viimeksi muutettu,...:
/*************************************************************************
*   Sovellus kahden luvun summan laskentaan, tekijä Olli Opiskelija
*  
*   muutettu viimeksi 26.1.1999 
**************************************************************************/



/////////////////////////////////////////////////////////////////////////////
// 
//  Formula 1 -simulaattorin pääohjelmaluokka, versio 12.B.36a (26.1.1999)
//
//  Copyright 1999 Orvokki Ohjelmoija
//                 Oy Simulaattori Ab
//
/////////////////////////////////////////////////////////////////////////////
Jälkimmäinen esimerkki on hyvin pelkistetty; yrityksissä on yleensä omat tiukat norminsa ja konventionsa ohjelmistojen kommentointiin.

Jos luokasta on tarkoitus luoda ilmentymiä, sen alkuun on syytä kirjoittaa lyhyt ja selkeä selostus luokan käyttötavasta. Myös metodien toteutuksen yhteydessä on hyvä mainita metodin tehtävä:

//--------------------------------------------------------------------
// Luokka KuuLaskuri toteuttaa laskurin, joka laskee kuukausien 
// numeroita 1, 2, ..., 11, 12, 1, 2, 3, ....
//
// Konstruktori:
//
//       public KuuLaskuri()  luo KuuLaskuri-olion, jonka kuukauden 
//                            numero on 1
//
// Julkiset metodit:
//
//    public int moneskoKuu()    palauttaa arvonaan KuuLaskuri-olion
//                               tietämän kuukauden numeron
//
//    public void seuraavaKuu()  edistää KuuLaskuri-olion kuukautta
//                               yhdellä
//--------------------------------------------------------------------
public class KuuLaskuri {

  private int kuu;   // sallitut arvot 1,..12

  // ----- konstruktori: -------

  public KuuLaskuri() { // luo KuuLaskuri-olion, jonka kuukauden
     kuu = 1;            // numero on 1
   }

  // ----- julkiset metodit: -----------

  public int moneskoKuu() {  // palauttaa arvonaan KuuLaskuri-olion
    return kuu;              // tietämän kuukauden numeron 
  }

  public void seuraavaKuu() { // edistää KuuLaskuri-olion kuukautta
    ++kuu;                    // yhdellä
    if (kuu == 13)
      kuu = 1;
  }

}


Jos muuttujien, metodien ja luokkien nimet on valittu hyvin, varsinaista ohjelmatekstiä ei tarvitse kovin paljon kommentoida.

     i = i + 1; // add one to i
olisi suorastaan typerää.

Mutta joissakin erikoistapauksissa yksittäisen lauseen kommentointikin voi olla hyödyllistä. Jos tekee jotakin kovin nokkelaa kommentti voi olla tarpeen - yleensä kuitenkin kannattaa suosia selkeyttä nokkeluuden kustannuksella.

Yksi erikoistapaus, jossa kommentointi on tärkeää, on metodin suorituksen lopettaminen jossakin muualla kuin lopussa:

  private static int hae(int[] taulu, int haettava) {

    for (int i=0; i<taulu.length; ++i) 
      if (taulu[i] == haettava)
        return i; // tärppäsi, lopetetaan

    return -1; // päästiin loppuun eikä löytynyt
  }

Nimiä muuttujille, metodeille, vakiolle ja luokille

"Tunnukseksi" kutsutaan nimeä, jonka ohjelmoija antaa jollekin nimettävälle asialle: muuttujalle, metodille, vakiolle, luokalle, ....

Javassa on tapana antaa muuttujalle ja metodille nimi, joka alkaa pienellä kirjaimella. Luokkien nimet aloitetaan isolla kirjaimella ja nimetyt vakiot kirjoitetaan kokonaan isoilla kirjaimilla. Näin nimestä näkee heti, mistä on kyse. Tätä tapaa on syytä noudattaa!

public class Luokka {

   public void metodi(int parametri) {
     final int VAKIO = 42;
     int muuttuja;
 ....

Tunnuksen nimen on ilmaistava tunnuksen käyttötarkoitus. Pitkätkin nimet ovat mahdollisia. Jos nimi muodostuu useasta sanasta, on "Java-tyylin" mukaista kirjoittaa sanan alku isolla:
  int ensimmäinenSyöttöluku, kissojenLukumäärä;
Tälla tavoin on nimetty Javan valmis kalusto (API).

Jotkut suosivat alaviivaa:

  int ensimmäinen_syöttöluku, kissojen_lukumäärä;

Muutamassa tilanteessa lyhytkin nimi voi olla hyvä. Esimerkiksi for-toistossa atk-ammattilaiset ovat aikojen alusta tottuneet käyttämään i:tä ja j:tä askeltamassa arvovälin yli. Kun näkee i:n "tietää että se on for:in muuttuja". Samoin hyvin paikallisesti käytetty muuttuja voi olla nimeltään vaikkapa "apu", jos avun tarvitsija erottuu selvästi:
    for (int i=0; i<taulu.length-1; ++i) 
      for (int j=i+1; j<taulu.length; ++i)
        if (taulu[i] > taulu[j]) {
          int apu  = taulu[i];
          taulu[i] = taulu[j];
          taulu[j] = apu;
        }

Tunnuksia voi nimetä suomeksi, ruotsiksi, englanniksi, ..., mutta on järkevää olla johdonmukainen. Jos ohjelma ei ole tarkoitettu kansainvälisesti ylläpidettäväksi, suomen- tai ruotsinkielisten tunnusten käyttämisessä on yksi etu: omat nimet eroavat Javan valmiin kaluston nimistä.

Alirakenteet ja ohjelmatekstin sisentäminen

Ohjelmointikielille on tyypillistä, että erilaiset rakenteet voivat sisältää alirakenteita, jotka voivat sisältää alirakenteita, ...

Javan luokka sisältää kenttiä, konstruktoreita ja metodeita. Kentän määrittely voi sisältää alkuarvojen asetuksia, metodit sisältävät muuttujien määrittelyitä ja muita lauseita, joista muodostuu metodin algoritmi.

Algoritmit sisältävät alialgoritmeja: while-lause voi toistaa if-lausetta, jonka else-osassa on toistolause, joka toistaa toistolausetta, joka sisältää if-lauseen, jne.

Ohjelman looginen rakenteisuus on syytä esittää ohjelman ulkoasun rakenteisuutena, jotta silmä voisi hahmottaa, mistä on kyse. Tapana on sisentää alirakenne:

public class Luokka {

   private int kissa = 7;

   public void hiiri() {
      int kärpänen;
      while (ulkonaTuulee())
         if (luntakinSataa())
            teeSitäSunTätä();
         else {
            teekinTota();
            jaTätäKans(); 
         }
      alaVähitellenLopetella();
   }
}

Pienin järkevä sisennys on kaksi merkkiä (omat silmäni ovat tottuneet tähän), kymmenen merkkiä on jo liian suuri sisennys: ohjelma alkaa vaeltaa kuvaruudun oikeaan laitaan.

Jotkut suosivat toisiaan vastaavien aaltosulkeiden kirjoittamista samalle sarakkeelle ja omalle rivilleen:

public class Luokka 
{
   private int kissa = 7;

   public void hiiri() 
   {
      int kärpänen;
      while (ulkonaTuulee()) 
         if (luntakinSataa()) 
            teeSitäSunTätä();
         else 
         {
            teekinTota();
            jaTätäKans();
         }
      alaVähitellenLopetella();
   }
}

Myös tämä tapa on selkeä. (Vaikka omat silmäni eivät olekaan tottuneet tähän.)

Tulostuslauseiden ulkoasusta

Tulostuslauseet on tyylikästä kirjoittaa siten, että ohjelmatekstistä näkee suoraan tulostuksen ulkoasun:
System.out.println("***************************************\n" +
                   "*        Tosi Hieno Ohjelma           *\n" +
                   "*        ------------------           *\n" +
                   "*                                     *\n" +
                   "*  komennot ovat : y, a, w, p         *\n" +
                   "***************************************\n" );

Parametreista

Jos metodilla on paljon parametreja, ne voi kirjoittaa alekkain:
  public String yksiMetoVaan( int kissa,
                              String hiiri,
                              PikkuVarasto koira,
                              BufferedReader hevonen) {
    ...

Javadoc-kommentoinnista

Varsinaisessa ohjelmistotuotannossa Javan tarjoama WWW-dokumentaation automaattinen generointi on hyvin käyttökelpoista, erityisesti kun laaditaan yleiskäyttöisiä luokkapakkauksia erityisten sovellusten toteuttamisen lähtökohdaksi. Esimerkiksi Javan API-dokumentaatio on toteutettu juuri javadoc-ohjelmalla.

Ns. dokumenttikommentti esitetään merkkiyhdistelmien /** ja */ välissä. Järjestelmä sisältää myös monia muita ilmauksia dokumentaation ohjaamiseen.

Luokka KuuLaskuri voitaisiin kirjoittaa muotoon:

/** Luokka KuuLaskuri toteuttaa laskurin, joka laskee kuukausien 
    numeroita 1, 2, ..., 11, 12, 1, 2, 3, ....
*/
public class KuuLaskuri {

   private int kuu;   // sallitut arvot 1,..12

   // ----- konstruktori: -------
   /**  luo KuuLaskuri-olion, jonka kuukauden
        numero on 1
   */ 
   public KuuLaskuri() {
     kuu = 1;
   }

  // ----- aksessorit: -----------

  /** palauttaa arvonaan KuuLaskuri-olion
      tietämän kuukauden numeron
  */
  public int moneskoKuu() {
    return kuu;
  }

  /** edistää KuuLaskuri-olion kuukautta
      yhdellä
  */
  public void seuraavaKuu() {
    ++kuu;
    if (kuu == 13)
      kuu = 1;
  }
}

Tällä määrittelyllä javadoc-ohjelma tuottaa mm. sivut KuuLaskuri.html, AllNames.html ja tree.html.

Javadoc esitellään perusteellisesti Sun-yhtiön sivulla How to Write Doc Comments for Javadoc.

...


Java and all Java-based marks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. University of Helsinki is independent of Sun Microsystems, Inc.