5 Olio-ohjelmointi 133 5. Olio-ohjelmointi Edellisissä luvuissa opittiin kirjoittamaan ohjelmia, joissa käytetään C# -kielen perustietotyyppejä, kuten int, double, float ja rakenteita, kuten operaattoreita, lausekkeita sekä ehto- ja toistolauseita. Perustietotyyppien avulla voidaan käsitellä yksinkertaisia arvoja, kuten lämpötilaa tai korkeintaan joukkoa yksinkertaisia arvoja yhdessä, kuten taulukkoa tuotteiden yksikköhinnoista. Monimutkaisempien arvojen käsittely, kuten työntekijöiden henkilötiedot, vaatii kuitenkin monimutkaisempia tietorakenteita (data structure). Tietorakenteet toteutetaan tyypillisesti luokkina (classes), jotka kuvaavat todellisen maailman ilmiöitä, kuten työntekijä, tuote, auto. Luokkiin yleensä kuuluu joukko perustietotyyppejä, jotka kuvaavat ilmiön ominaisuuksia (attributes), kuten työntekijän nimi ja metodeja (methods), jotka kuvaavat ilmiön toimintoja, kuten työntekijän palkanlaskenta. Tässä luvussa tutustutaan ensin tarkemmin luokkiin ja niiden ilmentymiin, eli olioihin (objects). Sen jälkeen kirjoitetaan ohjelmia, joissa luokkien ja olioiden avulla mallinnetaan reaalimaailman ilmiöitä ja ratkaistaan monimutkaisia ongelmia. 5.1 Luokat ja oliot Ympäröimiämme maailma muodostuu olioista (objects). Jokainen esine on olio, kuten kirja, tietokone ja auto. Olioilla on joukko ominaisuuksia tai attribuutteja (attributes), kuten nimi, väri, paino, ja niihin voidaan osoittaa tiettyjä toimintoja (actions). Toisin sanoen, ne pystyvät suorittamaan tiettyjä tehtäviä tai niitä käytetään tiettyihin tarkoituksiin, kuten ajamiseen. Olioita voidaan luokitella niiden yhteisten ominaisuuksien perusteella. Esimerkiksi polkupyörä on yleisnimike kaikille sellaisille esineille, joilla on tyypillisesti seuraavat ominaisuudet: runko, kaksi pyörää, kettingit, istuin, ohjaustanko sekä polkimet. Lisäksi polkupyörää käytetään pääasiallisesti paikasta toiseen siirtymiseen sitä polkemalla. Tämä kaikki tarkoittaa sitä, että vaikka jokainen pyörä voi näyttää tai jopa toimiakin hieman eri tavalla, se kuuluu polkupyörien joukkoon. Kuten polkupyöräesimerkistä voi huomata, jokainen olio kuuluu johonkin joukkoon tai luokkaan, eli sillä on samanlaisia ominaisuuksia ja käyttötarkoituksia joukon muiden olioiden kanssa. Olioiden yhteisten ominaisuuksien avulla voidaan kirjoittaa yleinen määritelmä niistä. Tämä määritelmä muodostaa yleiskäsitteen (abstract idea) olioista. Olio-ohjelmointi kehitettiin jo 1970-luvulla Xerox PARC:lla, ja moni aikaisempi ohjelmointikieli, kuten Smalltalk, Eiffel, Object Pascal, C++ ja Java ovat omaksuneet idean. Olio-ohjelmoinnissa pyritään matkimaan todellista maailmaa kirjoittamalla ensin määritelmiä (classes) ja sitten luomalla olioita (instances, objects) näiden määritelmien perusteella. Määritelmien avulla voidaan olioiden yhteiset attribuutit (attributes) ja tehtävät (metodit) yhdistää. Tätä kutsutaan kapseloinniksi (encapsulation). Näin todellisen maailman olioita voidaan helposti mallintaa tietokoneohjelmien avulla. 5.2 Luokan määrittely Luokka (class) kuvaa tietyn joukon olioita määrittelemällä niille yhteiset attribuutit (attributes) ja metodit (methods). Attribuutithan kuvaavat samaan kategoriaan tai luokkaan kuuluvien olioiden ominaisuuksia (kuten nimi, väri, pituus, paino) ja metodit puolestaan kuvaavat niiden toimintoja, tehtäviä tai käyttötarkoituksia (kuten opiskelu, istuminen, ajaminen,...). Esimerkiksi opiskelija on luokka, joka kuvaa kaikkia ihmisiä, jotka opiskelevat virallisesti jossakin korkeakoulussa ja joilla on etunimi, sukunimi, opiskelijanumero
134 C# - ohjelmointi sekä pääaihe, jota he opiskelevat. Näin ollen jokaisella opiskelija-luokkaan kuuluvalla on oltava edellä mainitut ominaisuudet (etunimi, sukunimi) ja toiminto (opiskeleminen). Vaikka olioilla on yhteinen määritelmä (class), se ei kuitenkaan tarkoita, että kaikki oliot olisivat ominaisuuksiltaan tai toiminnaltaan täsmälleen samanlaisia siten, että esimerkiksi kaikkien polkupyörien pyörät, polkimet ja ohjaustangot olisivat samanmittaisia tai samanvärisiä, tai kaikkia polkupyöriä käytettäisiin täsmälleen samanlaisiin tarkoituksiin. Attribuutit ja metodit mielletään luokan jäseniksi (class members). Luokan sisältö määräytyy sen jäsenten mukaan. Seuraavassa käydään ensin luokan määrittely yleisellä tasolla läpi, ja sen jälkeen opitaan määrittelemään attribuutit ja metodit sekä yhdistämään ne yhdeksi luokaksi. Luokan jäsenten näkyvyyttä sen ulkopuolelle voidaan rajoittaa saantimääreillä (access specifiers, access modifiers). Saantimääreillä voidaan luokka tai sen jäsenet laittaa rajoituksetta tai osaksi näkyviin tai piilottaa kokonaan. C#:ssa on viisi saantimäärettä, jotka vaikuttavat eri tavoin luokan jäsenen saatavuuteen (taulukko 5.1). C# ryhmittää ohjelman sisällön pakettiin, jota sanotaan assemblyksi. Assembly toteutetaan tyyppiä exe tai dll olevana tiedostona, joka sisältää ainakin tiedoston assembly manifest sekä kaikki tai osan ohjelmaan kuuluvista tiedostoista. Luokan attribuutit esitellään muuttujina, joita kutsutaan myös kentiksi (field). Ne määritellään luokan sisällä, kuten muuttujat muodossa [saantimääre] tietotyyppi muuttujan_nimi, jossa saantimääre on oletusarvoisesti private, tietotyyppi määrää muuttujalle tyypin eli varattavan muistin koon ja muuttujan_nimi on nimi, jolla varattuun muistipaikkaan viitataan. Esimerkiksi private bool tila esittelee bool-tyyppisen attribuutin, joka ei ole näkyvissä luokan ulkopuolella. Luokan tehtävät (behaviour) esitellään metodeina (methods), eli joukkona lauseita, jotka yhdessä suorittavat tietyn tehtävän ja voivat palauttaa jonkin arvon. Metodit määritellään luokan sisällä (syntaksi 5.1). [saantimääre] paluu_arvo metodin_nimi (parametri_lista) { // metodin_sisältö } Syntaksi 5.1. Metodin määrittely Taulukko 5.1 Saantimääreet Saantimääre Saatavuus public Luokan jäsen on saatavana rajoituksetta. protected internal Luokan jäsen on saatavana luokan sisällä, luokkaa perineen luokan sisällä tai samassa ohjelmassa (tai assemblyssa) olevan luokan sisällä. internal Luokan jäsen on saatavana luokan sisällä tai samassa ohjelmassa (tai assemblyssa) olevan luokan sisällä. protected Luokan jäsen on saatavana luokan sisällä tai luokan perineen luokan sisällä. private Luokan jäsen on saatavana vain luokan sisällä. Tämä saatavuusaste on oletuksena.
5 Olio-ohjelmointi 135 Metodin määrittelyssä (syntaksi 5.1) saantimääre toimii samalla tavalla kuin attribuuttien kanssa. Se on oletusarvoisesti private, jos sitä ei erikseen merkitä. Metodin määrityksessä paluu_arvo määrää metodin tulosteen (output) tietotyypin, eli sen tyypin, jollaista lataa metodi palauttaa, kuten int, bool, string ja niin edelleen. Metodi palauttaa arvon komennolle return. Jos metodi ei palauta mitään, kohtaan paluu_arvo kirjoitetaan void. Syntaksissa metodin_nimio on mikä tahansa sallittu muuttujan nimi. Jos C#:ssa metodin nimi koostuu kahdesta tai useammasta sanasta, niin jokainen sana kirjoitetaan isolla alkukirjaimella, kuten LaskeKeskiHinta. Syntaksissa sulkeiden sisällä parametri_lista on lista yhdestä tai useammasta tyyppi-muuttuja parista, jotka toimivat metodin syötteinä. Listassa tyyppi-muuttuja pari kirjoitetaan siten, että ensin tulee muuttujan tyyppi, sitten välilyönti ja sitten muuttujan nimi. Tyyppi-muuttuja-parit erotetaan toisistaan pilkulla, jos niitä on enemmän kuin yksi. Kommentilla merkityssä kohdassa metodin_sisältö on paikallisten muuttujien määrittelyt ja kaikki muut lauseet, jotka yhdessä suorittavat metodilla määritellyn tehtävän. Tarkastellaan metodin Laske- KeskiHinta() määrittelyä (kuva 5.1). Metodi saa syötteenä parametrit hinta1 ja hinta2, jotka ovat tyyppiä decimal. Metodi laskee näiden kahden luvun niiden keskiarvon, joka on myös tyyppiä decimal. Keskiarvo palautetaan metodin arvona (return). Luokka määritellään varatulla sanalla class (syntaksi 5.2). C#:ssa luokkien saantimääre on oletusarvoisesti internal, joka tarkoittaa, että luokka on saatavana saman assemblyn kaikissa luokissa. Luokall1 saantimääre public merkitsee sitä, että se on saatavana rajoituksetta. Luokan saantimääreeksi voidaan asettaa private, protected tai protected internal vain, jos se on määritelty toisen luokan sisällä (nested class). Syntaksissa kohta luokan_nimi (class name) on luokan nimi, joka voi olla mikä tahansa sallittu muuttujan nimi. Kuten metodien kohdalla luokkien nimet myös kirjoitetaan isoilla alkukirjaimilla. Syntaksissa kohta luokan_sisältö (class body) sisältää luokille määritellyt attribuutit ja metodit. Luokan sisältö muodostaa oman lohkon, joka tulee aina kirjoittaa aaltosulkeiden eli merkkiyhdistelmän { } sisään. Kuvassa 5.2 on esimerkki, jossa määritellään luokka Asunto, jolla on neljä attribuuttia: pintaala, huonemaara, vuokra ja vapaa sekä kaksi metodia TulostaTiedosto() ja VaraaAsunto(). Kuva 5.1. Vakion ja muuttujien arvon asetus arvoilla ja lausekkeilla [saantimääre] class luokan_nimi { // luokan_sisältö } Syntaksi 5.2 Luokan määrittely
136 C# - ohjelmointi 5.3 Olion luominen Kuva 5.2. Luokan Asunto määrittely Luokka on kuin mallinne (template), josta luodaan olioita. Kun luokka on määritelty, siitä voidaan luoda instansseja (instances) eli olioita (objects). Olioita luodaan operaattorilla new (kuva 5.3), joka varaa oliota varten tilan tietokoneen muistista. Muista, että ennen kuin oliolle on varattu tilaa tietokoneen muistista operaattorilla new, niin sitä ei saa missään tapauksessa käyttää. Ennen operaattorin new käyttöä olion arvo on null. Tämä tarkoittaa, että olio on käyttökelvoton ja sen käyttö aiheuttaisi virheilmoituksen. Olio voidaan merkitä käyttökelvottomaksi asettamalla sen arvoksi null. Esimerkiksi määrittämällä asunto=null olio asunto on käyttökelvoton. Kuva 5.3 Olion asunto luominen
5 Olio-ohjelmointi 137 Olio voidaan määritellä kahdessa vaiheessa, kuten kuvassa 5.3 tai yhdessä vaiheessa esimerkiksi lauseella Asunto asunto new Asunto(); Huomaa, kuinka yllä yhdenkin kirjaimen ero tekee kahdesta sanasta erilaiset. Asunto ja asunto tarkoittavat eri asioita. Asunto on luokan nimi ja asunto on olion nimi. Komentoa new seuraa muodostin (constructor). Muodostin luo olion. Muodostin on metodi, jolla on täsmälleen sama nimi, kuin luokalla ja jolla olion attribuutit alustetaan. Luokalla voi olla useita muodostimia, jotka eroavat toisistaan parametrien määrän ja/tai tyyppien mukaan. Oletusmuodostimella (default constructor) ei ole parametreja, ja sitä käytetään attribuuttien alustamiseen oletusarvoilla (default values). Jokaiselle luokille luodaan automaattisesti oletusmuodostin, eikä sitä tarvitse välttämättä määritellä. Ohjelmoija voi aina määritellä sen ja muut muodostimet haluamalla tavallaan. Kun olio on luotu, sillä on luokkansa mukainen tietorakenne, sillä on omat attribuutit ja metodit, jotka on esitelty sen luokassa. Olioiden attribuutteihin ja metodeihin viitataan pisteoperaattorilla (dot operator) siten, että ensin on olion nimi, sitten piste ja pistettä seuraa joko attribuutin tai metodin nimi (kuva 5.4). Jos attribuutin tai metodin saantimääre on private tai protected, siihen ei voida viitata suoraan olion nimen kautta. Olioiden attribuutit alustetaan tyypillisesti niiden luomisen yhteydessä. Mutta jos attribuutit ovat viitattavissa olion nimen kautta, ne voidaan myös alustaa olion luonnin jälkeen (kuva 5.5). Samasta luokasta voidaan luoda olioita mielin määrin (kuva 5.6). Pitää kuitenkin muistaa, että sitä mukaa, kun luodaan uusia olioita, muistista varataan uusia paikkoja. Siis jokaisella oliolla on muistissa oma tilansa, jolla ei ole mitään tekemistä muiden olioiden muistitilojen kanssa. Kuva 5.7 esittää, kuinka muistin varaaminen olioille tapahtuu Kuva 5.4 Olion attribuutteihin ja metodeihin viitataan pisteoperaattorilla Kuva 5.5 Olion attribuuttien alustaminen olion luonnin jälkeen
138 C# - ohjelmointi Kuva 5.6 Usean olion luonti samasta luokasta Kuva 5.7. Muistin varaaminen oliolle.