Modulaarisuus: periytyminen

Osaamistavoitteet

Tämän viikon jälkeen osaat selittää olion keskeiset ominaisuudet. Osaat myös selittää periytymisen peruskäsitteet ja ymmärrät periytymishierarkian ja abstraktin tietotyypin käsitteiden merkityksen ohjelmoinnissa.

Käsitteet luokka ja olio ovat jo tuttuja aiemmilta kursseilta ja ohjelmointikielistä. Ohjelmointi 1:llä käsiteltiin johdanto luokkiin ja olioihin. Ohjelmointi 2:lla puolestaan tutustuttiin periyttämisen alkeisiin lyhyesti. Seuraavaksi syvennämme ymmärrystä periytymisen roolista ohjelmoinnissa sekä tutustumme sen perusteisiin Java-kielessä.

Luokista ja olioista

Ohjelmoinnissa tarkastellaan asioita kahdesta näkökulmasta: rajapinnan ja toteutuksen. Olioita voidaan siten lähestyä sekä käyttäjän että toteuttajan kantilta. Olion julkinen rajapinta kertoo käyttäjälle, millaisia asioita olio pystyy tekemään. Olioiden kautta saadaan myös kätevä tapa toteuttaa olion tarjoama toiminnallisuus.

Olio kuuluu luokkaan

Jokainen olio kuuluu aina johonkin luokkaan. Luokka on se, joka määrittelee, miten kaikki tietyn luokan oliot käyttäytyvät. Luokka toimii moduulina eli se toimii tiedon kätkennän periaatteen mukaisesti. Luokka toimii myös tietotyyppinä – abstraktina tyyppinä, jolla voidaan tehdä tiettyjä operaatioita.

Luokka siis edustaa jokaista samalla tavoin rakentunutta oliota. Oliot ovat luokkamäärittelynsä tyyppisiä. Luokka määrittää olion tilan perusrakenteen (attribuutit) ja määrittelee rajapinnat olioiden käyttäytymisen määrittämiseksi (palvelut ja käyttäytyminen). Olion luominen eli olion instantiointi tapahtuu luokan määräämästi: syntyy olio, jolla on luokan määräämä sisäinen rakenne ja luokan määräämä alkutila. Luokka on ohjelman suunnitteluun ja toteutukseen tarvittava käsite. Ohjelman suorituksen aikana käytössä on lähes poikkeuksetta olioita. On siis hyvä lähestyä asiaa siten, että luokka on tapa kuvata yhteenkuuluvien olioiden yhteisiä ominaisuuksia. Lopullisessa ohjelmassa on aina luokan kuvaavan toiminnallisuuden toteuttavia olioita.

Oliolla on tila

Ohjelmassa olevilla olioilla on aina tila, joka muuttuu ohjelman ajon aikana. Olion tila koostuu luokan attribuuteista, jotka määrittävät mitä informaatiota olion tila tarvitsee. Olion tila tyypillisesti kapseloidaan julkisen rajapinnan taakse. Sitä voi tarkastella ja muuttaa vain julkisen rajapinnan kautta. Tila on myös jokaiselle oliolle oma ja muista olioista riippumaton.

Oliolla on identiteetti

Ohjelmassa jokainen olio pitää voida yksikäsitteisesti tunnistaa ja käsitellä. Ohjelmointikielissä tätä varten on olion identiteetin käsite. Identiteetti on esimerkiksi olion muuttujanimi tai muistipaikka, jossa olio sijaitsee. Identiteetti on merkityksellinen, koska sen avulla voidaan tarvittaessa tarkistaa, ovatko kaksi oliota erilliset ilman, että olion sen hetkisellä tilalla on vaikutusta tulokseen. Kahdella oliollahan voi hyvinkin olla tismalleen samakin tila jossain kohtaa ohjelman suoritusta, mutta ne silti ovat kaksi erillistä oliota.

Luokka

Luokka on tietotyyppi, joka määrittelee tarvittavat operaatiot sekä niiden toteuttamiseen vaadittavat tiedot. Samalla luokka voidaan nähdä moduulina, joka toteuttaa julkisena rajapintana operaatiot ja kätkee toteuttamiseen vaadittavat tiedot ja ominaisuudet rajapinnan taakse. Jokainen olio kuuluu aina johonkin luokkaan.

Olioista ja luokista (kesto 8:42)

Avainsana static

Kun ohjelmaan luodaan olio, syntyy luokasta uusi instanssi. Tämä tarkoittaa sitä, että jokaisella oliolla on omat kopionsa luokan jäsenmuuttujista. On kuitenkin tilanteita, joissa jokaiselle luokan oliolle halutaan pääsy yhteen yhteiseen jäsenmuuttujaan. Tällaista muuttujaa kutsutaan luokkamuuttujaksi. Luokkamuuttujien tarpeeseen törmää tilanteissa, joissa jokin kuuluu selvästi luokan vastuulle, muttei yksinään minkään luokan olion vastuulle. Tyypillisesti tällaiset ovat vakioita tai vaikkapa päiväysluokan tapauksessa luettelo kuukausien pituuksista. Niin Javassa kuin C++:ssakin luokkamuuttujat määritellään avainsanalla static. Esim.:

class Olio {
  public:
    Olio();
  private:
    static unsigned int oliot_;
};

unsigned int Olio::oliot_ = 0;
Olio::Olio() {
    //jokainen olio kasvattaa syntyessään yhteistä laskuria
    oliot_++;
}
public class Olio
{
  private static int oliot_;

  public Olio() { oliot_++; }
}

Vastaavasti voidaan määritellä luokkafunktioita, jotka kohdistuvat olion sijaan koko luokkaan luokkamuuttujien käsittelemiseksi.

Johdatus periytymiseen

Olio-ohjelmoinnin näkökulma keskittyy kapselointiin: olioiden ulkoiseen käyttäytymiseen sekä niiden sisäiseen toteuttamiseen. Olioita voidaan ryhmitellä molempien suhteen yhteen. Osalla olioista voidaan havaita olevan yhteisellä tavalla toimivaa ulkoista käytöstä. Yhteisen toteutuksen kautta ryhmittely taas säästää vaivaa: sama toiminnallisuus voidaan toteuttaa vain kerran. Luokkien jaottelua ja yhteisten ominaisuuksien muodostavien suhteiden käsittelyä kutsutaan periytymiseksi (engl. inheritance).

Terminologiasta

Olio-ohjelmoinnin periaatteesta engl. inheritance käytetään kahta suomennosta: osa puhuu perinnästä ja osa taas periytymisestä. Kurssilla olemme ottaneet lähtökohdan, jossa käytämme sitä termiä, johon olemme itse tottuneet. Kyse on pitkälti näkökulmasta ilmiöön: katsotaanko asiaa alhaalta ylös vai ylhäältä alas. Jos siis tekstissä törmäät perintään tai periytymiseen kyse on aina samasta ilmiöstä.

Luokka määrittää oliolle

Olion tila

Luokkamuuttuja