Pino teknisiä kirjoja

Osaaminen on jatkuvaa oppimista

Uskomme menetelmiin, jotka tukevat oppimista, kommunikointia ja järjestelmällistä tiimityöskentelyä. Menetelmiin, jotka auttavat meitä tekemään työmme paremmin, nopeammin ja harhailematta sivupoluilla.

Näillä sivuilla asiantuntijamme jakavat kokemuksiaan ja näkemyksiään eri teknologioista ja menetelmistä.

Artikkelit

Reaktor Twitter

Rajapintasuunnittelun perusteet

Rajapintasuunnittelu on hyvän koodaajan tärkeimpiä taitoja

Jokaisella ohjelmistokomponentilla on rajapinta. Hyvä rajapinta paljastaa komponentin toteutuksesta vain ne osat, joilla on merkitystä komponentin käyttäjille. Kaikki muu piilotetaan. Tällöin toteutuksen sisäiset muutokset eivät heijastu komponentin ulkopuolelle, vaan sitä voi käyttää "mustana laatikkona", jolloin käyttäjän ei tarvitse tietää, miten komponentti on sisäisesti toteutettu.

Abstraktiotason valinta on taidetta

Enkapsulointi ja datan piilottaminen ovat olio-ohjelmoinnin perusoppeja, mutta esimerkiksi tietotekniikan koulutuksessa näitä käsitellään yleensä vain yhden luokan tai moduulin näkökulmasta. Tällöin oikean abstraktiotason löytäminen on useimmiten triviaalia. Tehtävä muuttuu vaikeammaksi, kun työn alla olevassa softassa on yli miljoona koodiriviä ja se pitää jakaa sopiviin osakomponentteihin, jotka kommunikoivat keskenään hyvin määriteltyjen rajapintojen kautta. Komponentilla tarkoitan tässä mitä tahansa softan itsenäistä osakokonaisuutta, joka saattaa koostua sadoista luokista.

Juuri oikean abstraktiotason löytäminen on vaikeaa. Jos yksityiskohtia ja muistettavia asioita on liikaa, niiden ymmärtäminen on mahdotonta. Jos taas liikutaan liian korkealla abstraktiotasolla, siirrytään helposti puhumaan itsestäänselvistä yleisperiaatteista eikä enää olla kosketuksissa käytännön kanssa.

Tämä ei ole ohjelmointispesifinen ongelma. Samaan aiheeseen törmätään esimerkiksi määrittelyssä ja dokumentoinnissa sekä it-alan ulkopuolella esimerkiksi opetuksessa ja kaikenlaisessa ihmisten välisessä viestinnässä. Oikean abstraktiotason valinnan oppii vain käytännön harjoittelun kautta. Vaikkei oikotietä osaamiseen olekaan, tietyt nyrkkisäännöt voivat olla avuksi. Käyn seuraavaksi läpi muutamia tärkeimpiä.

Tämän artikkelin esimerkeissä on käytetty ohjelmointikielenä Javaa. Samat periaatteet kuitenkin pätevät kaikissa kielissä sekä erilaisissa integraatiorajapinnoissa (JSON/XML/jne.).

Estä virheelliset tilat

Komponentin alustamisen virheelliseen tilaan tulisi olla mahdotonta, tai ainakin se pitää tehdä mahdollisimman vaikeaksi.

Ei näin:

Component c = new Component();
// Jos tässä välissä kutsutaan komponentille c 
// jotain muuta operaatiota kuin initialize(), 
// päädytään virhetilanteeseen, koska 
// komponenttia ei ole vielä alustettu. 
c.initialize(new Configuration());
// Vasta nyt komponenttia voi käyttää. 

Vaan näin:

Component c = new Component(new Configuration());
// Nyt komponentti c on heti valmis käytettäväksi. 

Sama pätee tietysti kaikkiin muihinkin operaatioihin kuin alustukseen. Pitäisi olla mahdotonta saada ohjelmistokomponentti virheelliseen tilaan. Hyvää rajapintaa on mahdollisimman helppo käyttää oikein ja mahdollisimman vaikea käyttää väärin.

Tee operaatioista atomisia

Komponentille suoritettavien operaatioiden tulisi olla atomisia. Käytännössä tämä tarkoittaa sitä, ettei käyttäjän tarvitse muistaa, missä järjestyksessä kutsut pitää suorittaa.

Ei näin:

// Käyttäjän pitää muistaa aloittaa transaktio...
c.beginTransaction();
// Suoritetaan tarvittava operaatio, ja...
c.doSomething();
// Lopuksi pitää muistaa vahvistaa transaktio. 
c.commit();
// Entä mitä tapahtuu, jos operaatio 
// doSomething() päätyy virheeseen?

Vaan näin:

c.doInTransaction(new Callback() {
    @Override
    public void execute(Component c) {
        // Metodi doInTransaction() hoitaa 
        // transaktion aloittamisen ja 
        // vahvistamisen sekä mahdollisista 
        // virhetilanteista toipumisen. 
        c.doSomething();
    }
});

Tässä pätee sama nyrkkisääntö kuin edellisessä: tee helpoksi tehdä asiat oikein ja vaikeaksi tehdä asiat väärin.

Piilota monimutkaisuus

Komponentin rajapinnan tulee olla niin yksinkertainen kuin mahdollista, mutta ei sen yksinkertaisempi.

Jos pitää valita, otetaanko yksinkertainen rajapinta ja monimutkainen toteutus, vai monimutkainen rajapinta ja yksinkertainen toteutus, voittaa edellinen vaihtoehto aina. Rannekello on sisältä äärimmäisen monimutkainen laite, mutta silti ajan katsominen siitä on helppoa. Ohjelmistokomponentissa pitää pyrkiä samaan.

Rajapinnassa ei kuulu olla mitään turhaa. Esimerkiksi yleisessä käytössä oleva JavaBeans-malli, jossa luokan kentät paljastetaan get/set-metodeilla, on huono, sillä se paljastaa ulkomaailmalle turhia yksityiskohtia sisäisestä toteutuksesta.

Rajapinta on käyttöliittymä

Rajapintasuunnittelu on pohjimmiltaan käyttöliittymäsuunnittelua. Käyttöliittymä ei tässä tapauksessa ole graafinen, mutta rajapintaan pätevät silti samat perusperiaatteet kuin kaikkiin muihinkin käyttöliittymiin: sen pitää olla intuitiivinen, helposti opittava ja turhaa työtä minimoiva.

Huono koodaaja ei välitä koodinsa laadusta, kunhan se "toimii". Keskinkertainen koodaaja yrittää tehdä siistiä koodia, mutta ei ymmärrä, missä paikoissa monimutkaisuus on hyväksyttävissä ja missä ei. Hyvä koodaaja osaa jakaa koodinsa selkeisiin rajapintoihin ja sisäiseen toteutukseen, jossa saatetaan joskus tarvita monimutkaistakin koodia. Toki hyvä koodaaja pyrkii yksinkertaisuuteen ja selkeyteen kaikessa työssään, mutta hän tietää, missä kohdissa näistä vaatimuksista on mahdollista joustaa ja missä ei.

Ville Peurala lähikuvassa

Ville Peurala, pääarkkitehti

Ville Peurala on tehnyt softaa työkseen yli kymmenen vuotta. Hänen kokemuksensa mukaan laadukkaiden rajapintojen merkitys korostuu erityisesti suurissa ohjelmistoprojekteissa. Monimutkaisuus karkaa niissä helposti hallitsemattomaksi, mikäli laatuun ei kiinnitetä huomiota pienimmissäkin yksityiskohdissa.