Laatinut Jari Juslin, Jari.Juslin@iki.fi

Lyhyt olioiden tyyliohje

Olioiden laatimiseen ja Java-ohjelmointiin liittyvää ohjeistusta. Myös tämän oppaan mukaista käytäntöä vaadin keväällä pitämissäni Ohjelmoinnin harjoitustyö -ryhmissä.


----------

0. Tyylin perustelua

Koko olio-ohjelmoinnin perusidea tiivistyy hyvin laadittuun olioon. Ilman kelvollista olioihin jakoa ja ohjelmointikuria olioita tehdessä on mahdollista luoda oliokielillä huomattavasti karmeampia luomuksia kuin perinteisillä kielillä.

Oliokielten voima saadaan esiin vasta noudattamalla myös niillä toteutettavien ohjelmien suunnittelussa ja toteutuksessa hyviä olioperiaatteita. Joitakin niistä esittelen tällä sivulla.

Loppupään kohdat painottuvat yhä enemmän asioihin jotka tuntuvat äkkikatsomalta merkityksettömän pieniltä ja nipotukselta. Ne ovat kuitenkin sellaisia kohtia, joissa pienen vaivan säästämällä joutuu isoon vaivaan tulevaisuudessa. Vasta itse moisien laiskottelujen suossa aikansa rämmittyään ymmärtää niiden merkityksen.


----------

1. Olion rajaaminen

Ensimmäinen asia on olion rajaaminen. Olio-ajattelu lähtee siitä, että erillisen datan ja funktioiden sijasta paketoidaan sekä tieto että sen käsittelyyn tarvittavat operaatiot samaan pakettiin. Yksi olio sisältää siis jonkin järkevän kokonaisuuden tietoa (atribuutit ja alioliot) ja sen käsittelyyn tarvittavat operaatiot (metodit).

Karkeimmat virheet oliosuunnittelussa paljastuvat jo luokkien nimiä ja perustyönjakoa katsomalla. Jos luokan nimi on "AutonOvenAvaaja" tai "Renkaanvaihtaja" eikä "Auto" tai "Rengas" niin on ilmeistä että suunnittelija on paketoinut olioon operaatioita eikä tietoa.

Rakenteisessa ohjelmoinnissa on joukko operaatiokokonaisuuksia, joista laitetaan roikkumaan kulloistakin tietoa.

Olio-ohjelmoinnissa on joukko tietokokonaisuuksia, joista laitetaan roikkumaan kunkin tiedon tarvitsemat operaatiot.


----------

2. static:in käyttö

Luokkamuuttujia ja luokkametodeja tulee käyttää erittäin harkiten. static ei ole oikotie. Jos mietit että "näitä olioitahan ei ole kuin yksi, joten voin tehdä sen static:ina, lopeta heti. Nyrkkisääntö on, että jos jonkin voi tehdä olioina, se tehdään olioina.

static-määreen käyttäminen oikaisemaan olioiden välisessä tiedonkulussa johtaa ennemmin tai myöhemmin ongelmiin. Yllättävän nopeasti ajaudut tilanteeseen jossa oikeastaan haluaisitkin kaksi ilmentymää ko. luokasta. Esimerkiksi kaksi samanlaista sovelmaa samalle sivulle tai saman sovelman kahdelle eri sivulle, joita silti voitaisiin katsoa saman istunnon aikana molempia. static:cina toteutettua "oliota" on myös paha välittää parametrina.

Sitä varten oliosuunnittelua tehdään, jotta olioiden välinen kommunikointi ja työnjako hoituisi ilman vippaskonsteja.

static-määre on tarkoitettu kuvaamaan sellaisia ominaisuuksia joita luokalla ei oikeasti koskaan, missään ole kuin yhden kerran, tai jotka eivät liity yhteenkään olioon. Muista että luokkamuuttujia ei ole kuin se yksi, koko virtuaalikoneessa. Ja että et koskaan voi tietää milloin ohjelmaasi ajetaan useampaa kappaletta samassa virtuaalikoneessa..


----------

3. Jos käytät arvoa, määrittele aksessori

Älä koskaan (noh, on joitakin harvoja poikkeuksia) viittaa olion atribuutteihin ulkoa käsin. Määrittele kaikille ulkoa päin tarvittaville atribuuteille aksessorit.

Muista, että koskaan ei oliota pidä saada laittomaan tilaan sen metodeita käyttämällä. Käytä atribuutteja vain metodien kautta ja määrittele metodit niin että ne huolehtivat olion tilan säilymisestä järkevänä.


----------

4. Vältä kovakoodausta

Esimerkki1, älä tee näin:
/** Tämä luokka toimii
    esimerkkinä huonosta
    kovakoodauksesta. **/

class Luokka {
  int     taulukko      =
    new int[10];
  Button  nimiPainike   =
    new Button("Nimi");

/* ************************ */

  public void metodi() {
    for (int i=0; i < 10; i++) {
      foo(taulukko[i]);
    }

    if (aktiivinenPainike.
        getLabel() == "Nimi") {
      bar();
    }
  }
}
Esimerkki2, tee näin:
/** Tämä luokka toimii
    esimerkkinä kovakoodauksen
    välttämisestä. **/

class Luokka {
  public static final int
    TAULUKKO_MAX = 10;
  public static final String
    PAINIKKEEN_NIMI = "Nimi";
  int     taulukko      =
    new int[TAULUKKO_MAX ];
  Button  nimiPainike   =
    new Button(PAINIKKEEN_NIMI );

/* ************************ */

  public void metodi() {
    for (int i=0;
         i < TAULUKKO_MAX;
         i++) {
      foo(taulukko[i]);
    }

    if (aktiivinenPainike.getLabel()
         == PAINIKKEEN_NIMI) {
      bar();
    }
  }
}

Älä käytä "paljaita" numeroita tai merkkijonoja koodin seassa. Vakiomäärittelyt ovat vakioita varten, ja jos taas kyseessä ei ole vakio, sitä suuremmalla syyllä älä laita siihen kiinteää numeroa.

Kun myöhemmin sitten joudut muuttamaan jotakin vakioarvoa tai peräti muuttamaan sen arvon muuttujaksi, et enää edes muista missä kaikkialla sitä käytetään. Suomeksi sanottuna, muuttamalla sitä yhdestä kohdasta rikot koko ohjelmasi. Jos olet käyttänyt määriteltyä vakiota, selvitä ainakin pienemmillä ongelmilla.

Käyttämällä esimerkin 2 tapaa, et aja NullPointerException:eihin tai ArrayIndexOutOfBoundsException:eihin ainakaan silkkaa typeryyttäsi.


----------

5. Jaa myös metodit järkeviin kokonaisuuksiin

Omien kokemuksieni perusteella metodien järkevää työnjakamista helpottaa kun kirjoittaa ensin metodille dokumenttikommentin. Tällöin joudut väkisin ensin miettimään että mitähän ihmettä se metodisi oikein tulee tekemään ja sitten vasta toteutat sen.

Metodin järkevä kokonaisuus ei yleensä ole yli 30 rivinen. Metodin järkevä kokonaisuus ei juuri koskaan ole yli 50 rivinen. Sinun ohjelmasi ei ole se juuri koskaan.

Jos metodillasi ei ole yhtä selkeää toimenpidettä jonka se suorittaa, ehkä se pitäisi jakaa osiin tai koostaa metodit toisin.


----------

6. Vältä logiikan kirjoittamista main()-metodiin

Esimerkki:

class Luokka {

  public static void
      main(String args[]) {

    new Luokka(args).start();

  }

}

Tyylipuhtaimmillaan main() ainoastaan luo ilmentymän isäntäluokastaan ja antaa sen hoitaa ohjelman suorituksen. Poikkeuksena esimerkiksi sovelmien main(), jossa yleensä myös luodaan ikkuna isäntäluokkaa varten.

Joka tapauksessa, en pidä hyvänä tyylinä suorittaa mitään prosessointia main():issa. Jos isäntäluokan pitää prosessoida jotain, se luo säikeen sitä varten.

Tähän käytäntöön on perusteluina kolme eri syytä:

  1. Kyseessä on olio-ohjelma, tarkoitus on että oliot tekevät kaiken mahdollisen.
  2. static-osioita tulee ylipäänsäkin välttää ja niissä suoritettava koodi minimoida. Jos jonkin voi tehdä yhtä hyvin ilmentymämetodissa, tee se ilmentymämetodissa.
  3. Luokan yleiskäyttöisyyden kannalta on eduksi, jos luokka osaa konstruktorissaan ja mahdollisessa omassa säikessään suorittaa oleellisen toimintansa. Ei ole järkevää että luokkaa mahdollisesti myöhemmin käyttävä ohjelma joutuisi kutsumaan sitä käyttämällä main():ia.


----------

etusivu - Java-sivu - olioiden tyyliohje

----------