Olio-ohjelmointi

Wikipedia
Ohjattu sivulta Oliopohjainen ohjelmointikieli
Loikkaa: valikkoon, hakuun

Olio-ohjelmointi (object-oriented programming) on ohjelmoinnin lähestymistapa, jossa ohjelmointiongelmien ratkaisut jäsennetään olioiden yhteistoimintana. Oliot sisältävät toisiinsa loogisesti liittyvää tietoa ja toiminnallisuutta.

Olio-ohjelma muodostuu kokoelmasta yhteistyössä toimivia oliota, kun taas perinteinen tietokoneohjelma on lista ohjeita tietokoneelle. Olio-ohjelmoinnissa jokainen olio pystyy vastaanottamaan viestejä, käsittelemään tietoa, ja lähettämään tietoa muille olioille. Jokainen olio voidaan nähdä itsenäisenä pienenä koneena, jolla on tietty rooli tai vastuu.

Olio-ohjelmointi on kehitetty helpottamaan ja selkeyttämään yhä monimutkaisemmiksi tullutta ohjelmistojen kehittämistä ja ylläpitoa. Olio-ohjelmoinnin yhtenä tavoitteena on tehdä ohjelmistoista helpompia laajentaa ja ylläpitää. Yksittäinen olio vastaa jostakin itsenäisestä osa-alueesta ohjelman kokonaisuudessa. Olio on merkityksellinen myös irrotettuna välittömästä asiayhteydestään, jolloin sitä voidaan käyttää myös uusissa asiayhteyksissä. Oliota käyttävän tarvitsee vain tietää mihin tarkoitukseen olio on suunniteltu ja miten sitä pitää käyttää. Koska yhden pienen olion sisäinen ohjelmakoodi on lyhyt, se on yleensä helpommin ymmärrettävissä ja ylläpidettävissä.

Tietokoneohjelmat voivat hyödyntää olioita monella tapaa. Puhtaissa oliokielissä kaikki kielen alkiot ovat olioita. Useat ohjelmointikielet, kuten C++ sallivat olioiden käyttämisen yhdessä perinteisen ohjelmointimallin sisällä. Oikein käytettynä olio-ohjelmointi nopeuttaa kehitystyötä, vähentää redundanssia, ohjelmointivirheitä ja helpottaa merkittävästi ohjelmistojen ylläpitoa. Nykyaikaisten tietokoneohjelmien kehittämisessä olio-ohjelmointi on yksi tärkeimmistä työkaluista. Suosittuja olio-ohjelmointia tukevia kieliä ovat mm. C++, C#, Java, Python ja Visual Basic.

Käsitteitä[muokkaa | muokkaa wikitekstiä]

Olio (object) on ohjelmiston perusyksikkö, joka sisältää joukon loogisesti yhteenkuuluvaa tietoa ja toiminnallisuutta. Oliot voivat kommunikoida keskenään lähettämällä ja vastaanottamalla viestejä. Viestin vastaanottaminen suorittaa määritellyn toiminnon vastaanottavassa oliossa. Oliota käytetään ohjelmistosuunnittelussa esittämään jonkun abstraktin tai reaalimaailman käsitteen ilmentymää ohjelmistossa. Oliokielillä laaditut ohjelmat koostuvat tavallisesti lukuisista olioista, joiden yhteistoiminnan tuloksena on ratkaisu ohjelmointiongelmaan. (kts. myös oliomalli)

Luokka (class) määrittelee jonkun tietyn oliojoukon yhteiset piirteet. Olio on luokan ilmentymä, instanssi. Esimerkiksi henkilörekisteriohjelmassa määritellään luokka Henkilö, joka määrittelee millaista tietoa henkilöistä halutaan esittää ja millä tavalla näitä tietoja voidaan käsitellä. Ohjelman ajon aikana luokasta Henkilö luodaan olioita esittämään yksittäisiä henkilöitä. Olioiden toiminnallisuus määritellään useimmiten luokkien jäsenfunktioissa, jolloin kaikki saman luokan oliot sisältävät täsmälleen saman toiminnallisuuden tiedon käsittelyyn. On olemassa myös vähemmän tunnettuja prototyyppipohjaisia kieliä, jotka eivät tunne luokkakäsitettä.

Perintä (inheritance) mahdollistaa uusien luokkamääritysten tekemisen vanhojen määritysten pohjalta. Periyttämisen ansiosta on mahdollista käyttää uudestaan jo tehtyä ohjelmakoodia uusien luokkien pohjana. Periyttäminen mahdollistaa myös abstraktien perusluokkien käytön, joista periytetään useita luokkia, joita kaikkia voidaan käyttää samannimisillä funktioilla ja samoilla parametreilla kuin abstraktia perusluokkaa. Periyttäminen ja abstraktien perusluokkien käyttö ovat oliopohjaisten kielien vahvimpia mutta myös vaikeimpia ominaisuuksia. Periyttäminen mahdollistaa abstraktit rajapinnat, joiden kautta voidaan käsitellä tuntemattomien tahojen rakentamia olioita ennaltamääritellyllä tavalla.

Moniperintä (multiple inheritance), mahdollistaa uuteen luokkaan periyttämisen useista kantaluokista. Vaikka tämä voi vaikuttaa epäloogiselta, on se hyödyllistä esimerkiksi tilanteessa, jossa on "lentävä otus"- ja "nisäkäs"-luokat. Jos halutaan tehdä objekti, joka on "nisäkäs", mutta "osaa lentää", voidaan se periyttää molemmista luokista, jolloin se perii molempien ominaisuudet. Moniperintä ei ole mahdollista kaikissa oliokielissä, mutta esimerkiksi Common Lisp- ja C++-kielissä se on tuettu.

Kapselointi (encapsulation) -termiä käytetään kahdessa merkityksessä. Ensimmäisen merkityksen mukaan kapseloinnin ideana on datan ja käyttäytymisen kokoaminen yhteen yksikköön, olioon. Toinen merkitys lisää tähän vielä tiedonpiilotuksen: olion sisäisiin muuttujiin ei (pääsääntöisesti) päästä suoraan käsiksi olion ulkopuolelta, jolloin ohjelmointivirheiden määrä vähenee. Olion käyttäjän ei tarvitse tietää enää käyttövaiheessa sitä, miten olio toimii sisäisesti: riittää kun tietää, miten oliota itseään käytetään ja miten olio käyttäytyy. Olion käyttäjät voivat käyttää oliota sen julkisen rajapinnan kautta (public interface). Olion luokkakuvauksessa voi olla myös yksityisiä muuttujia tai funktiota (private interface), jotka ovat vain olion itsensä käytettävissä.

Metodi (method), jäsenfunktio (member function) tai operaatio (operation) tarkoittavat luokassa määriteltyä aliohjelmaa, joka käsittelee olion tietoa.

Attribuutti (attribute), jäsenmuuttuja (member variable) tai kenttä (field) tarkoittavat luokassa oliolle määriteltyä muuttujaa, joka tallentaa jonkun olioon liittyvän tietoalkion.

Rakentaja (constructor) on metodi, jota kutsutaan oliota luotaessa. Rakentaja alustaa olion jäsenmuuttujien alkuarvot.

Hajottaja tai purkaja (destructor) on metodi, jota kutsutaan olion tuhoamiseksi. Hajottaja vapauttaa olion varaamat resurssit.

Esimerkki[muokkaa | muokkaa wikitekstiä]

Pseudokielinen esimerkki luokkakuvauksesta luokalle "Oppilas":

 // Aloitetaan kantaluokan määrittely
 class Henkilö {
 protected:
  String nimi; // yhteinen jäsenmuuttuja kaikille henkilöille
 public:
   function asetaNimi( String nimi){
    this.nimi = nimi;
   }
 }
// Periytetään luokka Opettaja kantaluokasta.
class Opettaja inherits Henkilö {
 public:
  function haeTitteli(){
   return "prof. " + this.nimi; 
  }
}
// Periytetään luokka Opiskelija kantaluokasta
class Opiskelija inherits Henkilö {
 private:
   int arvosana; // sisäinen muuttuja, jota ainoastaan olio itse voi käsitellä
 // tästä eteenpäin kirjoitetut julkiset tiedot ovat kaikkien käytettävissä
 public:
 // olion julkinen funktio
 function asetaArvosana(int uusiArvosana) {
   this.arvosana = uusiArvosana; // laitetaan parametri "uusiArvosana" sisäiseen muuttujaan "arvosana"
 }
 function haeArvosana() {
   return this.arvosana;   // palautetaan sisäisen muuttujan "arvosana" arvo
 }
 function haeTitteli(){
   return "tekn. yo. " + this.nimi; 
  }
}

Esimerkki olion käytöstä:

// Tehdään kaksi oliota "harri" ja "pekka"
// HUOM! Jokaisella uudella oliolla on omat yksityiset muuttujansa
harri = new Opiskelija();
pekka = new Opiskelija();
prof = new Opettaja(); 
prof.asetaNimi("Tuumivainen");
harri.asetaArvosana(5);
harri.asetaNimi( "Harri Hakkeri" );
pekka.asetaArvosana(4);

// Tulostaa: Harrin arvosana on 5 ja Pekan on 4
print "Harrin arvosana on " + harri.haeArvosana() + " ja Pekan on " + pekka.haeArvosana();
// Tulostaa: opettaja oli: prof. Tuumivainen
print "opettaja oli: " + prof.haeTitteli();
// Tulostaa: opiskelija oli: tekn.yo. Harri Hakkeri
print "opiskelija oli: " + harri.haeTitteli();

Oliokielten kehitys[muokkaa | muokkaa wikitekstiä]

Ole-Johan Dahl ja Kristen Nygaard kehittivät ensimmäisen oliokielen, Simulan, Norjan valtion tietojenkäsittelykeskuksessa 1960-luvulla. Simulassa esiteltiin olioiden, luokkien ja perinnän käsitteet. Tämän jälkeen on kehitetty satoja oliokieliä, joista tässä esitellään muutamia tärkempiä:

Varsinainen olio-ohjelmointi (sen nimisenä siis: object-oriented programming) syntyi Xeroxin perustamassa kehityslaboratoriossa, Palo Alto Research Centerissä (PARC). Tämän työn tuloksena syntyi puhdas olio-ohjelmointikieli Smalltalk 1970-luvun lopulla. Kielen isä ja työnjohtaja oli Alan Kay. Tämä kieli on vaikuttanut keskeisesti kaikkiin muihin oliokieliin, joita on kymmeniä.

Seuraava merkittävä olio-ohjelmoinnin kielihaara syntyi, kun tanskalainen Bjarne Stroustrup kehitti C++-kielen puhelinjätti Bellin laboratoriossa 1983. C++-kieli pohjautuu suositun C-kielen syntaksiin, joten C++ oli luonnollinen valinta monille ohjelmoijille. C++:n kehityksessä tärkeänä tavoitteena oli ohjelmien hyvä suorituskyky, joten ohjelmointiympäristön ajon aikaiset tarkistukset ja tukirakenteet pyrittiin minimoimaan. C++:ssa tyyppitarkastukset tehdään käännösaikana, joten kieli on staattisesti tyypitetty. C++ on edelleen suosittu suurta tehokkuutta vaativissa sovelluksissa, kuten tietoliikenteessä, langattomissa päätelaitteissa ja sovellusohjelmissa.

Sun Microsystemsin palveluksessa ollut James Gosling kehitti 90-luvun alussa C++:aa muistuttavan Java-ohjelmointikielen. Lähtökohdiltaan Java poikkeaa C++:sta. Java on laitteistoriippumaton, sillä Java-tavukoodiksi käännetty ohjelma voidaan ajaa Java-virtuaalikoneessa millä tahansa laitealustalla, jolle virtuaalikone on toteutettu. Ajon aikana Java on myös raskaampi, sillä se tukee automaattista muistinhallintaa, inspektiota, reflektiota ja C++:aa laajempia ajonaikaisia tarkastuksia. Toisaalta Java-kielestä jätettiin pois tiettyjä C++-kielen piirteitä, kuten moniperintä, joiden katsottiin tekevän kielestä liian monimutkaisen. Java on nykyään suosittu yleiskäyttöisenä ohjelmointikielenä ja erinomaiset ohjelmistokehitysvälineet mahdollistavat korkean työn tuottavuuden ohjelmistotyössä.

Python-kieli on tullut suosituksi vuosituhannen vaihteen jälkeen. Python on tulkattu ja dynaamisesti tyypitetty ohjelmointikieli, jossa tyyppitarkastukset tehdään ajon aikana. Pythonia pidetään varsin puhdasoppisena oliokielenä ja se on rakenteeltaan yksinkertainen. Pythonia käytetään eniten pienimuotoisten prototyyppien nopeaan kehitykseen.

Olio-ohjelmoinnin periaatteita[muokkaa | muokkaa wikitekstiä]

Olio-ohjelmoinnissa pyritään olioiden hyvään koheesioon. Olioiden tulee vastata jotakin yksiselitteistä ja selkeärajaista käsitettä. Olion tietojäsenet liittyvät kiinteästi toisiinsa. Esimerkiksi opintorekisteritietojärjestelmässä ei samaan olioon tallenneta opiskelijan ja hänen käymiensä kurssien tietoja, vaan opiskelijaolio sisältää pelkästään opiskelijan tiedot ja viittaukset opintosuoritusolioihin.

Olioiden välisten riippuvuuksien hallinta on tärkeää laajoissa oliojärjestelmissä. Oliolla on riippuvuus kaikkiin niihin olioihin, joille se lähettää viestejä. Olioiden tulisi kommunikoida vain muutamien läheisesti olioon liittyvien olioiden kanssa. On vältettävä tilannetta, jossa järjestelmän kaikki oliot joutuvat olemaan tietoisia suuresta määrästä muita olioita eri puolilta järjestelmää.

Olioita pyritään ohjelmoimaan niiden rajapintoja vastaan. Rajapinta kuvaa olion tarjoamat palvelut, eli mitä olio tekee. Olion toteutuksen yksityiskohdat, eli miten olio toimii, pyritään piilottamaan muilta olioilta. Käytännössä olion rajapinta koostuu sen julkisista metodeista ja jäsenmuuttujista. Oliolla voi olla myös suojattuja metodeja ja jäsenmuuttujia, jotka eivät ole käytettävissä olion ulkopuolelta. Usein jäsenmuuttujien käsittely olion ulkopuolelta estetään ja olion tilaa voidaan käsitellä vain sen jäsenfunktioiden avulla.

Rajapintaluokat (interface) määrittelevät pelkästään rajapinnan ilman toteutusta. Rajapintoluokkien käyttö mahdollistaa tietyn toiminnon toteutuksen varioimisen ohjelmassa. Konkreettiset luokat, jotka perivät rajapintaluokan, voivat toteuttaa rajapintaluokan määrittelemän rajapinnan haluamallaan tavalla.

Olio-ohjelmoinnin menetelmiä[muokkaa | muokkaa wikitekstiä]

Olio-ohjelmointi on vaikuttanut suuresti ohjelmistotuotannon menetelmiin ohjelmiston elinkaaren kaikissa eri vaiheissa.

Olioanalyysi on keskeinen osa olio-ohjelmiston määrittelyä ja suunnittelua. Olioanalyysissa selvitetään ongelma-alueen (problem domain) käsitteet (concepts) ja niiden väliset rakenteelliset ja toiminnalliset suhteet. Olioanalyysin tuloksena on käsitemalli (concept model), josta toteutuksen luokkamäärittelyt voidaan suoraviivaisesti johtaa.

Olioanalyysia varten on kehitetty useita mallinnuskieliä. Mallinnuskielet poikkeavat monella tavalla tavallisista oliokielistä: Mallinnuskielet eivät ole suoritettavia, vaan ne kuvaavat järjestelmän rakennetta korkeammalla abstraktiotasolla ilman yksityiskohtia. Mallinnuskieliä käytetään pääasiassa graafisella notaatiolla, mikä mahdollistaa suurten kokonaisuuksien havainnollistamisen diagrammien avulla. Mallinnuskielten rakenne on kuitenkin tarkoin määritelty, jotta mallit eivät olisi tulkinnanvaraisia. Object Management Groupin (OMG) Unified Modeling Language (UML) on saavuttanut hallitsevan aseman yleiskäyttöisten mallinnuskielien joukossa.

Suunnittelumallit (design patterns) syntyivät alun perin rakennusarkkitehtuurin piirissä, mutta niiden idea otettiin käyttöön olio-ohjelmoinnissa 1990-luvun alussa. Suunnittelumalli on toistettavissa oleva ratkaisu johonkin suunnitteluongelmaan tietyssä asiayhteydessä. Suunnittelumallin kuvaus sisältää ainakin nimen, kuvauksen olosuhteista joissa sitä voi soveltaa, ratkaisun kuvauksen ja ratkaisun seuraukset. Yleisesti tunnettujen nimettyjen ratkaisumallien käyttö tehostaa viestintää ohjelmistosuunnittelijoiden välillä ja parantaa varsinaisten toteutusten selkeyttä ja rakennetta.

Olioteknologioiden käyttö ohjelmiston suunnittelussa ja toteutuksessa ei ohjaa minkään tietyn prosessimallin käyttöön tai muuta ohjelmiston laadunvarmennuksen perusperiaatteita. Olioteknologian asiantuntijat ovat kuitenkin osallistuneet aktiivisesti keskusteluun ohjelmistoprosesseista. Ketterät menetelmät, käyttötapausmallinnus ja testivetoinen kehitys syntyivät pienessä Smalltalk-kehittäjien yhteisössä, josta ne ovat levinneet yleisempään käyttöön vuosituhannen vaihteen jälkeen.

Olio-ohjelmoinnin sovellusalueita[muokkaa | muokkaa wikitekstiä]

Olio-ohjelmoinnin ensimmäisiä käyttökohteita olivat diskreetit tapahtumasimulaatiot, joissa mallinnettiin esimerkiksi puhelinjärjestelmien tai logististen järjestelmien toimintaa.

Olio-ohjelmointi soveltuu erityisen hyvin graafisten käyttöliittymien toteuttamiseen. Käyttöliittymäkirjastot, kuten Swing, SWT tai MFC sisältävät luokkahierarkian, joissa käyttöliittymän komponenttien yhteisiä ominaisuuksia hallitaan perinnän avulla.

Olio-ohjelmointi on hallitseva paradigma myös tietojärjestelmien suunnittelussa. Reaalimaailman käsitteet mallinnetaan olioanalyysissä ja käsitteitä vastaavat luokat toteutetaan ohjelmointikielellä. Yleinen ongelma tietojärjestelmissä on olioiden avulla esitetyn mallin tallentaminen relaatiotietokantaan, sillä relaatiomalli voi esittää käsitteiden välillä monesta moneen suhteita, mutta ei toisaalta pysty esittämään perintäsuhteita.

Hajautetuissa oliosovelluksissa oliot on sijoitettu hajalleen eri palvelinten välille. Olioiden välinen viestintä hoidetaan sovitun protokollan, esimerkiksi SOAPin, CORBA tai RMI:n avulla. Olion luominen ja purkaminen tapahtuvat näennäisesti oliota kutsuvalla koneella, mutta itse olion suorittama proseduuri voidaan ajaa jollakin toisella tietoverkossa sijaitsevalla palvelimella.

Olio-ohjelmoinnin ongelmia[muokkaa | muokkaa wikitekstiä]

Kaikista eduistaan huolimatta olio-ohjelmointi ei ole täysin ongelmatonta. Siinä missä proseduraalisessa ohjelmassa yhdessä kohtaa esiintynyt virhe saattoi vaikuttaa vain paikallisesti, olio-ohjelmoinnissa virheet monistuvat välittömästi kaikkiin oliota käyttäviin tahoihin. Olio-ohjelmoinnissa hyvän suunnittelun ja automatisoidun yksikkötestauksen merkitys korostuu. Väärin käytettynä olioilla voi myös olla suorituskykyvaikutuksia. Koska jokainen olio rakennetaan aina kokonaisuudessaan tietokoneohjelman muistiin, suurten oliomäärien käsittely voi viedä huomattavasti muistia.

Olio-ohjelmoinnin perusfilosofian (ns. ohjelmointiparadigma) opettelu vie myös usein aikaa. Perinteisestä proseduraalisesta ohjelmoinnista olio-ohjelmointiin siirtyminen on usein haasteellista ja on joskus vaikeaa jopa kokeneille ohjelmistosuunnittelijoille. Nykyisin olio-ohjelmointia opetetaan useilla eri koulutustasoilla ja peruskäsitteet hallitaan yleisesti ottaen paremmin kuin ennen. Tästä huolimatta moni ohjelma suunnitellaan näennäisesti olioilla, mutta käytännössä ne noudattavat samaa vanhaa ohjelmointityyliä, johon aikaisemmin on opittu. Olio-ohjelmointi ei siis tee kenestäkään parempaa ohjelmoijaa eikä automaattisesti anna mitään etua suhteessa perinteiseen ohjelmointiin, ellei näitä etuja osata oikealla tavalla hyödyntää.

Luokkien periyttämisen heikkoutena on eri luokkien käytännön toteutuksen sitominen toisiinsa, mikä saattaa aiheuttaa ongelmia siinä vaiheessa kun ohjelmistosta tehdään uusia versioita. Näiden ongelmien ennaltaehkäisemiseksi on tehty suunnittelumalleja, jotka perustuvat hyväksi havaittuihin ratkaisuihin.

Olio-ohjelmointia tukevia kieliä[muokkaa | muokkaa wikitekstiä]

Kirjallisuutta[muokkaa | muokkaa wikitekstiä]

  • Sytykeraportti: Oliot systeemityössä. Suomen Atk-Kustannus, 1998. ISBN 951-762-165-5.
  • Kai Koskimies: Oliokirja. Suomen Atk-Kustannus, 2000. ISBN 951-762-720-3.
  • Bertrand Meyer: Object-Oriented Software Construction. Prentice-Hall, 1998. ISBN 0-13-629155-4.
  • Markku Sakkinen: Inheritance and Other Main Principles of C++ and Other Object-oriented Languages. Jyväskylä Studies in Computer Science, Economics and Statistics 20. University of Jyväskylä, 1992.
  • Jouni Smed, Harri Hakonen, Timo Raita: Sopimuspohjainen olio-ohjelmointi Java-kielellä. Elektroninen kirja, 2007. ISBN 978-952-92-1776-2
  • Peter Coad/Jill nicola: Object-Oriented Programming. Prentice Hall, 1993. ISBN 0-13-032616-X.
  • Erich Gamma, Richard Helm, Ralph Jonson, John Vlissides: Design Patterns. Olio-ohjelmointi. Suunnittelumallit. Edita, IT Press, 2001. ISBN 951-826-428-7.