Olio-ohjelmointi Johdanto olio-ohjelmointiin Ohjelmistoa kehitettäessä voidaan tunnistaa ainakin kaksi abstraktiota: prosessiabstraktio ja dataabstraktio. Prosessiabstraktio huomattiin jo varhain, koska algoritmien ja operaatioiden abstrahointi on välttämätöntä hallittaessa hiemankaan laajempia ohjelmakokonaisuuksia. Dataabstraktio keksittiin, kun havaittiin tarve yhdistää tietotyyppeihin operaatioita, joiden yksityiskohtia tietotyypin käyttäjän ei tarvitse tuntea. Data-abstraktio toteutetaan abstraktien tietotyyppien avulla; nämä tietotyypit puolestaan johtivat olio-ohjelmoinnin syntyyn. 1. Abstraktit tietotyypit Abstrakti tietotyyppi (abstract data type, ADT) on tietotyyppi, joka toteuttaa seuraavat ehdot ([Seb, s. 437]): 1. Tiedon kapselointi: Tyypin määrittely samoin kuin tietotyyppiin liittyvät operaatiot on määritelty yhdessä syntaktisessa yksikössä. Muut ohjelmayksiköt voivat muodostaa tämän tyypin muuttujia. 2. Tiedon kätkentä: Tyypin määrittelyn ja operaatioiden yksityiskohdat eivät näy tietotyypin ulkopuoliselle käyttäjälle. Kapselointi helpottaa laajan ohjelman teknistä ja loogista hallintaa, kun kootaan data ja sitä käsittelevät operaatiot yhteen. Tällöin ohjelman mentaalinen hahmottaminen on helpompaa. Lisäksi ohjelma voidaan jakaa yksiköihin, jotka ovat suhteellisen riippumattomia toisistaan. Tällöin muutos yhdessä osassa ei välttämättä edellytä muiden osien muuttamista tai kääntämistä. Tiedon kätkentä lisää tietotyypin luotettavuutta. Kun vain tietotyypin sisäiset operaatiot voivat väliaikaisesti rikkoa tietotyypin ilmentymän, ohjelmointivirheiden mahdollisuus pienenee. Koodin ylläpidettävyys helpottuu, koska tietotyypin sisäisten operaatioiden muuttuessa sen ulospäin näkyvä rajapinta säilyy muuttumattomana. Kun lisätään abstrakteihin tietotyyppeihin periytyminen ja monimuotoisuus, päädytäänkin olioohjelmointiin. 2. Historiaa Kun ohjelmistotuotanto laajeni 1960- ja 1970-lukujen vaihteessa, havaittiin myös monia ongelmia ohjelmointiprojekteissa. Oli tavallista, että projektit eivät valmistuneet ajoissa tai ollenkaan ja niiden budjetit ylittyivät. Lisäksi tuotetut ohjelmat olivat usein huonolaatuisia. Tätä tilannetta alettiin kutsua ohjelmistokriisiksi (the software crisis). Siitä lähtien on kehitetty monia ohjelmistotuotannon prosesseja ja ohjelmointiparadigmoja ongelman ratkaisuksi. Olioparadigma ja siihen liittyvät prosessit ovat nykyään varsin suosittu menetelmä. Paradigman käyttö suosii erityisesti koodin uudelleenkäytettävyyttä. Toimiviksi havaittujen ohjelmistokomponenttien uudelleenkäytön uskotaan myös lisäävän ohjelmien luotettavuutta. Kristen Nygaard ja Ole-Johan Dahl kehittivät Simula-kielen Norjan laskentakeskuksessa simulointitarkoituksiin vuonna 1961. Aluksi esiprosessori käänsi Simula-ohjelmat Algolkielisiksi. Kuitenkin Simula I:ssä käytettiin jo luokkia ja niistä luotuja olioita. Vuonna 1967 Simula I sai seuraajan, ensimmäisen varsinaisen olio-ohjelmointikielen, Simula67:n.
Simula67:ssä luokat ja niistä luodut oliot olivat peruskäsitteitä. Luokkia voitiin myös periä uusiin luokkiin. Kieli ei kuitenkaan levinnyt yleiseen käyttöön. Olio-ohjelmointikielet yleistyivät vasta 1980-luvulla. Suuri vaikutus myöhempiin oliokieliin on ollut Alan Kayn kehittämällä Smalltalkilla. Alan Kay työskenteli 1970-luvulla Xeroxin Palo Alton tutkimuskeskuksessa. Hän kehitti graafiseen käyttöliittymään pohjautuvaa henkilökohtaista tietokonetta, Dynabookia. Tämä oli erittäin radikaali ajatus 1970-luvun alussa, jolloin tietokoneet olivat suuria ja käyttöliittymät merkkipohjaisia. Smalltalk oli puhdas olioohjelmointikieli, jonka avulla Dynabookia hallittiin. Smalltalk ei ollut pelkästään kieli, vaan täydellinen ohjelmointijärjestelmä, ts. kielen ja ohjelmointiympäristön muodostama kokonaisuus. Smalltalk-kieliset ohjelmat koostuvat olioista, jotka kommunikoivat lähettämällä toisilleen viestejä. Kaikki luokat periytyvät viime kädessä yhteisestä Object-luokasta. Xerox aloitti Smalltalk-kielen jakelun vuonna 1980, mutta tämäkään kieli ei saavuttanut merkittävää asemaa ohjelmistotuotannossa. Alan Kay siirtyi Applelle, jossa hän vastasi Apple Macintoshin moniikkunaisesta hiirellä ohjattavasta käyttöliittymästä. Olio-ohjelmointi tukevoitti 1980-luvulla asemaansa ohjelmistotuotannossa sikäli, että silloin kehiteltiin myös olioparadigmaan sopivia tuotantoprosesseja suunnittelun ja ohjelmoinnin lisäksi. Vuosikymmenen alkupuolella syntyi ensimmäinen tuotantokäytössä yleistynyt olioohjelmointiin suunniteltu kieli: C++. Bjarne Stroustrup kehitti sen vuonna 1980 AT&T Bellin laboratoriossa nimellä C with classes lisäämällä C-kielen päälle oliotuen. Vuonna 1983 kieli otettiin käyttöön myös Bellin laboratorioiden ulkopuolella ja Rick Mascitti keksi nimen C++. Kieltä kehitettiin voimakkaasti 1980-luvulla; siihen lisättiin mm. moniperintä, parametrisoidut tyypit ja poikkeusten käsittely. C++ on standardisoitu vuonna 1997; standardi sisältää itse kielen ja C++:n standardikirjaston. C++ ei ole puhdas oliokieli vaan hybridi: sitä voidaan käyttää usean eri paradigman mukaiseen ohjelmointiin. C++-kieleen tehtiin laaja standardimuutos vuonna 2011: määriteltyä standardia kutsutaan nimellä C++11. Se on aiempien versioiden kanssa yhteensopiva, mutta se sisältää merkittäviä uudistuksia kieleen, mm. monisäieohjelmoinnin ja säännöllisten ilmausten tuki lisättiin. C++-kielen uusin standardi on joulukuussa 2014 hyväksytty C++14, joka oli huomattavasti maltillisempi uudistus. Suurempi muutos on tulossa vuonna 2017. Tällä kurssilla kehitettävät ohjelmat noudattavat kuitenkin enimmäkseen vuoden 1997 standardia. Java ilmestyi 1990-luvun puolivälissä. Sen pääasiallinen kehittäjä oli Sun Microsystemsillä työskennellyt James Gosling. Kieli kehitettiin alun perin elektronisten pienlaitteiden sulautetuksi ohjelmointikieleksi, koska C++:aa pidettiin liian epäluotettavana mm. siksi, että C++-ohjelmissa esiintyy yleisesti ohjelmoijan virheestä aiheutuvia muistivuotoja. Java pohjautuu C++-kieleen, mutta siitä on karsittu pois ohjelmointivirheitä aiheuttavia piirteitä. Kieli ei salli moniperintää ja kaikilla luokilla on yhteinen kantaluokka Object. Toisin kuin C++-kielessä, Javassa ohjelmoijan varaama käyttämätön muisti vapautetaan automaattisesti. Lisäksi osoitintyypistä on Javassa luovuttu. Lisäominaisuutena aiempiin C++-versioihin on rinnakkaisen ohjelmoinnin tuki. Java on myös puhtaampi oliokieli kuin C++, perustietotyypit eivät Javassa kuitenkaan ole olioita. Javasta ei tullut suosittua sulautettujen järjestelmien ohjelmointikielenä vaan siksi, että se soveltui ominaisuuksiensa vuoksi www-ohjelmointiin. 2000-luvulla on syntynyt ainakin yksi suosittu oliokieli, Microsoftin vuonna 2002 lanseeraama C#, joka on.net-alustassa käytettävä kieli. C# muistuttaa varsin paljon Javaa; James Gosling
onkin arvostellut kieltä Java-imitaationa. Myös oliosuuntautunut skriptikieli Python on saavuttanut 2000-luvulla suurta suosiota; sen oliotuki on kuitenkin hieman rajoittunut. Lisätietoa olio-ohjelmointikielistä löytää esimerkiksi lähteistä [Har] ja [Seb]. 3. Peruskäsitteistön esittelyä ja merkintöjä Luokka on olio-ohjelmoinnin peruskäsite. Se on ohjelmoijan määrittelemä tietotyyppi, joka toteuttaa tiedon kapseloinnin ja kätkennän. Olio on luokasta tehty ilmentymä, oliot ovat siis ohjelmassa luokan tietotyyppisiä muuttujia. Luokan jäsenet voivat olla jäsenmuuttujia tai metodeja. Jäsenmuuttujat eli attribuutit määräävät luokan sisältämän datan, ne muistuttavat ohjelmointikielen muuttujia. Metodit taas muistuttavat funktioita ja ne määräävät luokan operaatiot. Näihin kuuluvat sekä palvelut, jotka luokka tarjoaa sen käyttäjille että luokan sisäiset operaatiot. Tiedon kätkentä toteutetaan antamalla luokan jäsenille näkyvyysmääreitä, jotka säätelevät miten jäsen näkyy luokan ulkopuolelle. Näkyvyys koskee sekä jäsenmuuttujia että metodeja. C++-kielessä näkyvyysmääreet ovat private, protected ja public. Näiden määreiden merkitystä käsitellään tarkemmin osassa Olioperusteinen ohjelmointi C++-kielellä. Private-tyyppiset jäsenet ovat käytettävissä ainoastaan luokan sisällä mutta public-tyyppiset kaikkialla. Protectedtyyppiset attribuutit näkyvät luokassa ja sen aliluokissa eli luokan perivissä luokissa. Javakielessä on lisäksi ns. oletusnäkyvyys, jota käytetään, mikäli näkyvyysmäärettä ei anneta lainkaan. Itse asiassa näkyvyys on sekä C++:ssa että Javassa hieman monimutkaisempaa, mutta siihen ei puututa tässä. Public-tyyppiset attribuutit muodostavat luokan ulospäin näkyvän rajapinnan. Yleensä tämä koostuu julkisista metodeista, jotka määräävät luokan käyttäjilleen tarjoamat palvelut. Luokan palveluja käytetään siten kutsumalla sen olioiden julkisia metodeja. Yleensä jäsenmuuttujia ei sisällytetä luokan julkiseen rajapintaan vaan jäsenmuuttujia manipuloidaan ainoastaan tähän tarkoitukseen laadittujen metodien avulla. Tällöin voidaan nimittäin varmemmin valvoa, ettei olion sisäinen tila rikkoudu. Ohjelmien kuvaamiseen ja niiden mallintamiseen käytetään nykyisin useimmiten UMLkuvauskieltä (Unified Modeling Language). Se on käytössä myös tällä kurssilla ja rinnakkaisella kurssilla Oliosuuntautunut analyysi ja suunnittelu. Viimeksi mainitulla kurssilla perehdytään kielen mahdollisuuksiin tarkemmin. Ohjelman luokkarakenne esitetään UML:ssä luokkakaaviolla. Alla on esitetty UML-merkinnällä luokka TMBaseClass, jolla on yksi yksityinen jäsenmuuttuja, yksi julkinen metodi ja kaksi suojattua metodia. Muuttujat ja metodit erotetaan vaakaviivalla. Kuvioon on myös lisätty huomautus, joka kertoo metodin templatemethod() toiminnan. Javan oletusnäkyvyystyypin (pakkausnäkyvyyden) symbolina käytetään merkkiä ~. Esimerkki luokkakaaviosta.
Usein olemassa olevaa luokkaa halutaan erikoistaa ja muodostaa uusi luokka. Tällöin olemassa oleva luokka peritään uuteen luokkaan eli aliluokkaan, joka perii kantaluokkansa ominaisuudet. Joissakin kielissä (esimerkiksi C++:ssa) luokka voi periä useammankin luokan; toisissa (kuten Javassa) periytyminen on rajoitettu yhteen luokkaan. Seuraavassa on UML-merkinnällä esitetty tilanne, jossa ConcreteStrategy-luokka perii Strategy-luokan. Periytyminen UML-merkinnällä. Periytymistä käytetään yleensä, kun uuden luokan oliot ovat erikoistapauksia kantaluokan olioista. Esimerkiksi yllä olevassa kaaviossa voidaan olettaa, että luokan ConcreteStrategy oliot ovat eräänlaisia Strategy-olioita. Mikäli luokka perii (toteuttaa) rajapinnan, voidaan nuolessa käyttää katkoviivaa. Periytymistä käsitellään tarkemmin osassa Periytyminen ja monimuotoisuus. Monesti näkee periytymistä käytettävän sellaisessakin tapauksessa, että uuden luokan olio itse asiassa käsitteellisesti sisältää kantaluokan olion. Tällöin on loogisempaa käyttää koostetta; alkuperäisen luokan olio (tai useampia olioita) on attribuuttina uudessa luokassa. Esimerkiksi yllä Context-luokan olio sisältää yksityisenä attribuuttinaan Strategy-luokan olion. Koosteen erikoistapaus on aito kooste eli kompositio, jolloin UML-notaatiossa käytetään valkean salmiakkikuvion asemasta mustaa. Aidon koosteen tapauksessa komponentin suhde pääluokkaan on seuraava: 1. Komponentti voi olla olemassa ainoastaan pääluokan olion osana. 2. Komponentti voi olla osana ainoastaan yhdessä pääluokan oliossa. Koosteolioita käsiteltäessä pitää kiinnittää erityishuomiota olioiden kopioimiseen ja niiden yhtäsuuruuden vertailuun. Kooste on erikoistapaus luokkien välisestä assosiaatiosta, joka on kahden tai useamman välinen suhde, viittaus tai riippuvuus. Assosiaatiota merkitään luokkien välisellä janalla, jossa voi olla myös nuoli merkitsemässä lukusuuntaa. Assosiaatiolle voidaan antaa nimi ja lisäksi merkitä kertautuminen, ts. niiden olioiden lukumäärä, joita assosiaatio voi vähimmillään ja enimmillään yhdistää.
Esimerkki Composite-suunnittelumallin luokkakaavio. Edellä oleva esimerkkikaavio kuvaa ns. Composite-suunnittelumallin rakennetta. Siinä Client käyttää Component-luokan operation()-metodia. Tämä assosiaatio on merkitty kaavioon. Kaaviossa näkyy myös aito kooste: Component-oliot sisältyvät johonkin Composite-säiliöön. Lähteet [Har] Harsu, Maarit. Ohjelmointikielet, Periaatteet, käsitteet, valintaperusteet, Talentum 2005. [Seb] Sebesta, Robert W. Concepts of Programming Languages 5th edition, Addison-Wesley 2002