Luokka (ohjelmointi)

olion tyyppi olio-ohjelmoinnissa
Tämä artikkeli käsittelee olio-ohjelmoinnin luokkaa. Sanan muita merkityksiä on erillisellä täsmennyssivulla.

Luokka (class) tarkoittaa olio-ohjelmoinnissa olion tyyppiä.[1] Luokassa määritellään ohjeet, joiden mukaan luodaan ohjelman ajon aikana sen instanssi eli olio (object). Kyse on käytännössä laajennettavasta muotista, jota käytetään olioiden luomiseen. Muotti eli luokka voi luoda itsestään yleensä rajattoman määrän olioita ajon aikana[2]. Luokassa määritellään siitä muodostettavien olioiden rakenne ja toiminnallisuus. Siinä siis kerrotaan millaista tietoa olio sisältää ja määritellään metodit, joiden avulla oliota voidaan käsitellä. Yhdessä luokassa voi olla monta oliota.


Jokainen olio on jonkin luokan ilmentymä eli instanssi (instance). Saman luokan eri ilmentymät omistavat keskenään identtiset metodit, mutta ilmentymiin säilötty data on uniikki jokaiselle ilmentymälle ja ilmentymän metodit käsittelevät tätä uniikkia dataa. Luokka määrittää nämä metodit ja kertoo minkä tyyppiset datakentät jokaiselta luokan ilmentymältä löytyy. [2]

Rakenne

muokkaa
 
UML merkintätapa luokalle

Datakentät

muokkaa

Datakentät käsittävät käytännössä sen, mitä tietoja luokasta muodostettavat instanssit eli oliot (object) omaavat. Datakenttien tietoja taas käsitellään metodien avulla.

Metodit

muokkaa

Luokan tai sen ilmentymien käyttäytyminen määritellään sen metodien avulla. Metodien avulla määritetään luokasta muodostettujen olioiden toiminnot. Nämä toiminnot voivat muuttaa olion tilaa tai yksinkertaisesti tarjota tapoja siihen pääsemiseksi.

Luokkien periaate

Jokainen luokka toteuttaa eli implementoi rajapintaa (engl. interface) tarjoamalla rakennetta ja toimintaa. Rakenne koostuu datasta ja tilasta. Toiminta koostuu koodista, mikä puolestaan tarkentaa, miten metodit ovat implementoitu. Rajapinnan ja kyseisen rajapinnan implementoinnin määritelmät poikkeavat toisistaan. Monissa ohjelmointikielissä rajanveto näiden välillä on kuitenkin häilyvä, sillä luokkien esittely sekä määrittelee että implementoi rajapintaa. Jotkut ohjelmointikielet tarjoavat ominaisuuksia, jotka erottelevat rajapintaa ja implementointia. Abstrakti luokka on esimerkki tästä: sillä voidaan määritellä rajapinta tarjoamatta samalla implementaatiota.

Periytymistä tukevat ohjelmointikielet mahdollistavat sen, että luokat pystyvät perimään rajapintoja luokista, joista ne ovat itse peräisin.

  • Esimerkiksi 1. “Luokka A” periytyy “luokasta B” ja jos “luokka B” implementoi rajapintaa “rajapinta B”, “luokka A” myös perii “rajapinnassa B” määritellyn toiminnallisuuden eli sen vakiot ja metodit.

Näkyvyysmääreitä (engl. access specifiers) tukevissa ohjelmointikielissä luokan rajapinnan katsotaan olevan joukko julkisia luokan jäseniä. Näihin lukeutuu metodit ja muuttujat. Mitään luokan sisällä olevia yksityisiä (engl. private) jäseniä tai sisäisiä tietorakenteita ei ole tarkoitettu riippuvaisiksi ulkoisesta koodista ja täten ne eivät ole osa rajapintaa.

Olio-ohjelmoinnin metodologiaan kuuluu, että minkä tahansa luokan rajapinnan operaatiot ovat itsenäisiä ja riippumattomia toisistaan. Tuloksena on kerroksittainen rakenne, jossa käytetään rajapinnassa määriteltyjä metodeja. Rajapinta ei aseta vaatimuksia sille, että rajapinnan operaatioita tulisi kutsua tietyssä järjestyksessä. Etuna tässä lähestymistavassa on, että käyttäjän koodi voi olettaa rajapintojen operaatioiden olevan saatavilla silloin, kun käyttäjällä on pääsy olioon.

Jäsenten saatavuus

muokkaa

Olio-ohjelmoinnissa on kolme perusjäsentyyppiä jotka ovat[3]:

  • Yksityinen (private): rajoittaa luokan saavutettavuutta sen ulkopuolelta. Ainoastaan luokan sisäiset metodit pääsevät käsiksi yksityisiin jäseniin.
  • Suojattu (protected): mahdollistaa jäsenen käsittelyn itse pääluokalle ja kaikille sen jäsenluokille.
  • Julkinen (public): antaa kaikelle koodille mahdollisuuden päästä jäseneen käsiksi.

Suurin osa oliopohjaisista kielistä tukevat edellä mainittuja näkyvyysmääreitä, mutta niiden käyttömääritelmät voivat olla erilaisia eri kielillä.

Yleensä näkyvysmääreitä käytetäänkin niin, että sisäinen rakenne tehdään yksityiseksi, mutta jäsenelle tehdään julkinen apumetodi, jonka avulla dataan päästään käsiksi. Näkyvyysmääreet eivät itsessään hallitse jäsenen näkyvyyttä, vaan sitä mitkä metodit pääsevät jäseneen käsiksi.

Eri oliopohjaiset kielet tukevat jäsenten saavutettavuutta ja näkyvyyttä eri tasoilla. Riippuen kielen säännöistä ja kääntämiskäytännöistä jäsenten käsittely voidaan mahdollistaa joko kääntämisen tai ajon aikana. Esimerkiksi Java ei mahdollista koodin kääntämistä, jos koodissa pyritään viittaamaan jonkin luokan yksityiseen dataan[4]. C++ kielessä taas yksityiset metodit ovat nähtävissä, mutta niihin ei voi päästä käsiksi rajapinnalla. Metodeista voidaan kuitenkin tehdä kokonaan näkymättömiä erityisesti määrittelemällä täysin abstrakteja luokkia, jotka edustavat luokan rajapintoja.

Muita saavutettavuustapoja:

  • Instanssin vs luokan saavutettavuus: Ruby ohjelmointikieli tukee instanssin yksityisiä ja suojattuja saavutettavuusmääreitä (instance-private ja instance-protected) vastaavien luokkamääritteiden sijaan. Nämä eroavat sillä tavalla, että saavutettavuus on rajoitettu itse instanssiin perustuen, eikä instanssissa olevaan luokkaan perustuen.
  • Friend: C++ tukee tapaa, jossa funktio, joka on erityisesti nimetty luokan ystäväfunktioksi (friend function), pääsee käsiksi jäseniin, jotka ovat muuten määritelty yksityisiksi tai suojatuiksi.
  • Polkuun perustuva: Java tukee saavutettavuuden rajoittamista tiettyyn jäseneen Javan paketin sisällä, joka tarkoittaa tiedoston loogista polkua. Kuitenkin kun Javan rakennetta jatketaan, rakennetaan yleensä kaikki luokat tietyn paketin sisältä yhdeksi luokkarakenteeksi, jotta kaikkiin suojattuihin jäseniin pästään käsiksi. Tarkoittaen sitä, että lähdetiedosto johon viitataan voi sijaita täysin eri sijainnissa, mutta se on silti saman loogisen polun sisällä, jolloin kaikkiin sen jäseniin päästään käsiksi.

Luokan ja olion suhde

muokkaa

Ajatus luokan ja olion suhteesta pohjautuu Platonin ideaoppiin. Olio vastaa Platonin aistimaailman jäljitelmää. Luokka eli idea antaa rakennusohjeet olion muodostamiselle: se määrittelee olion rakenteen ja käyttäytymisen. Havainnollistava esimerkki on piparkakkumuotti: luokka on kuin metallinen piparimuotti, jolla tehdään taikinasta pipari-olioita. Kukin pipari on erilainen, vaikka kaikissa käytettiin samaa mallia. Esimerkissä huomataan myös luokan rooli: vaikka luokka-piparimuotilla on mahdollista tehdä pipareita, ei luokka kuitenkaan itse ole syötävä pipari, vaan pelkkä mallina toimiva muotti. Konkreettinen ero syntyy myös ohjelmaa ajettaessa: luokat kirjoitetaan ohjelmakoodiin, oliot syntyvät luokista vasta sitten kun ohjelma ajetaan.

  • Esimerkiksi 1. Talon pohjapiirustus on luokka, jonka pohjalta voidaan luoda olio talo.
  • Esimerkiksi 2. DNA on luokka jonka pohjalta voidaan luoda olio proteiini.

Tietokoneohjelmissa luokassa määritellään lisäksi tiedot siitä, miten olioita käytetään sen jälkeen kun ne on valmistettu.

Olion muodostus

muokkaa

Jotta luokasta voidaan muodostaa olio, tarvitaan kolmas osapuoli, joka lukee ja tulkitsee luokan ja muodostaa siitä toimivan olion. Esimerkiksi talon pohjapiirustus ei yksinään tee taloa, vaan tarvitaan vielä lisäksi

  • Raaka-aineita
  • Osaavaa työvoimaa, joka osaa rakentaa kohteen

Tietokone-ohjelmien tapauksessa raaka-aineena toimii tietojärjestelmän muisti (paikallinen tai hajautettu), jonne olioiden tiedot talletetaan. Työvoimana toimii tietokoneen prosessori, joka lukee olion piirustuksista kuinka se rakennetaan. Olion valmistusvaiheessa tietokone varaa osoite-avaruuden, jonne olio lopullisesti sijoitetaan sekä sen yhteyteen olion yhteyssä etukäteen määritellyt funktiot (proseduurit). Olioiden funktioiden toteutustapa on proseduraalinen ohjelmointikielilähde? ja niiden erikoispiirteenä on, että nämä funktiot pystyvät lukemaan myös niitä muuttujia, jotka on määritelty yksityiksiksi tiedoiksi (engl. private variables).

Ohjelmistojen laadun näkökulmasta on tärkeää miettiä, mitä metodeja tai dataa luokat paljastavat muulle ohjelmistolle. Oliopohjaiset kielet sisältävät yleisesti jonkin keinon rajoittaa datakenttien ja metodien näkyvyyttä kyseisen luokan ulkopuolelle. Tämän näkyvyyden rajaaminen ja luokan tarkkojen vastuiden määrittäminen tunnetaan nimellä enkapsulaatio (encapsulation). [2]

Luokan paljastamat metodit ja datakentät kertovat ulkopuoliselle ohjelmistolle, mitä luokasta luoduilla olioilta voi odottaa ja mitä niillä voi tehdä. Tämä muodostaa sopimuksen luokan ja muiden luokan paljastamia metodeja tai datakenttiä käyttävien luokkien välillä. Sopimusta kutsutaan nimellä rajapinta (interface). [2]

Ohjelmiston peruslaatua voidaan arvioida myös käsitteillä koheesio (cohesion) ja pariutuminen (coupling) [2]. Yksi olio-ohjelmoinnin keskeisimmistä asioista on myös pitää huolta siitä, että jokainen luokka suorittaa yhden ja vain yhden tehtävän. Tällä tarkoitetaan luokan rajapinnan pitämistä minimaalisena, eli korkeaa kapselointia. [2]

Koheesiota voidaan arvioida luokkakohtaisesti luokan metodien ja datakenttien yhteistoiminnan perusteella. Mitä tiiviimmin luokan metodit ja datakentät ovat yhteistyössä, sitä korkeampi koheesio luokalla on. Korkea koheesio kulkee pitkälti käsi kädessä tiukan kapseloinnin kanssa, sillä mitä vähemmän luokka paljastaa itsestään muulle ohjelmistolle, sitä tiukemmin sen metodit ja datakentät kommunikoivat keskenään. Korkea koheesio sekä kapselointi ovat siis tavoiteltavia ominaisuuksia. [2]

Pariutuminen tarkoittaa ohjelmiston luokkien riippuvuutta ohjelmiston toisista luokista. Pariutumisesta puhutaan usein asteikolla löysä (loose) - tiukka (tight). Löysästi pariutuneessa ohjelmistossa jokainen luokka on riippuvainen vain muutamasta muusta luokasta, ja tämä on tavoiteltavaa ohjelmiston laadun kannalta. [2]

Korkea koheesio johtaa monesti löysään pariutumiseen ja matala koheesio taas tiukkaan pariutumiseen. Kapselointi mukailee yleensä koheesiota niin, että korkeasta koheesiosta seuraa korkea kapselointi ja toisin päin. [2]

Periytyminen

muokkaa

Olio-ohjelmoinnissa käytetään käsitettä periytyminen kun uusi luokka saa (perii) jo olemassa olevan luokan ominaisuudet. Periytymisen ansiosta ohjelmoinnin työmäärä vähenee, kun aiemmin tehtyjä luokkia voidaan käyttää hyväksi vain tarpeelliset komponentit lisäämällä. Sellaista luokkaa, josta on periytetty muita luokkia, kutsutaan periytetyn luokan yläluokaksi (tai kantaluokaksi), ja periytettyä luokkaa vastaavasti alaluokaksi (tai aliluokaksi). Alaluokassa voidaan peittää yläluokan ominaisuuksia sekä lisätä uusia.

Esimerkiksi luokka "kissa" voi periä luokan "eläin". Tällöin kissaluokalla on samat ominaisuudet ja toiminnot kuin eläinluokalla. Tässä tapauksessa eläin olisi yläluokka ja kissa alaluokka.

  • Esimerkiksi 1. Piirto-ohjelmassa voisi olla luokka 'kuvio' ja tällä aliluokat 'kolmio' ja 'ympyrä'. Ikkunaa piirrettäessä käydään läpi listaa 'kuvio'-luokkaan kuuluvista olioista ja pyydetään jokaista oliota piirtämään itsensä. Jokainen kuvio piirtää itsensä oikein sen mukaan, onko kyseessä 'kolmio' vai 'ympyrä'. Kun ikkunan piirto ei ota kantaa siihen, mitä kuvioita ikkunassa on, on helppo laajentaa ohjelmaa periyttämällä kuvio-luokasta vaikkapa 'neliö'-luokka.

Joissakin ohjelmointikielissä yhdellä luokalla voi olla vain yksi yläluokka, joissakin taas rajattomasti. Jos yläluokkia voi olla useita, käytetään termiä moniperintä. Moniperintää tukemattomissa ohjelmointikielissä, kuten Javassa, ongelman voi kiertää rajapintaluokkia käyttämällä.

Joissakin ohjelmointikielissä on olemassa myös abstrakteja luokkia, joista ei voi luoda ilmentymiä (olioita).

Hyödyt

muokkaa

Ohjelmoinnissa käytettävät luokat auttavat asioiden hahmottamisessa. Näin ongelma pystytään jakamaan osaongelmiin.

Ohjelmiston jakaminen objektiluokkiin tuo kolme päähyötyä:

  • Nopea kehitys
  • Ohjelman ylläpidon ja huollon helppous
  • Koodin ja mallien uudelleenkäyttö

Objektiluokat nopeuttavat ohjelmiston kehitystä, koska ne vähentävät semanttista kuilua koodin ja käyttäjien välillä. Järjestelmän analyytikot voivat puhua sekä kehittäjien, että käyttäjien kanssa käyttäen periaatteessa samaa sanastoa. Luokan ilmentymiä voidaan tarkastella ohjelman ajon aikana varmistaakseen, että järjestelmä, ja sen sisältämät ominaisuudet toimivat odotetulla tavalla. Lisäksi useimmat objektiorientoituneet ympäristöt tarjoavat tulkittuja testaus- ja virheenkorjaustyökaluja, joiden avulla kehittäjä voi analysoida tarkalleen, missä ohjelman virhe tapahtui ja nähdä, mitkä metodit kutsuttiin millä argumenteilla.

Objektiluokat helpottavat ylläpitoa kapseloinnin avulla. Kun kehittäjien on muutettava jonkin objektin käyttäytymistä, muutos voidaan rajoittaa vain siihen objektiin ja sen osiin. Tämä vähentää ei-toivottujen sivuvaikutusten riskiä huoltotoimenpiteiden aikana.

Ohjelmiston uudelleenkäyttö on myös tärkeä etu objektiluokkien käytössä. Luokat helpottavat uudelleenkäyttöä perimisen ja rajapintojen avulla. Kun uusi käyttäytyminen on tarpeen, se voidaan usein saavuttaa luomalla uusi luokka, joka perii yläluokan oletuskäyttäytymisen ja -datan ja mukauttamalla jonkin käyttäytymisen tai datan ominaisuutta vastaavasti. Uudelleenkäyttö rajapintojen kautta tapahtuu, kun toinen objekti haluaa kutsua jonkin objektiluokan metodia sen sijaan, että luo uuden ohjelman. Tämä menetelmä poistaa monia yleisiä virheitä, joita voisi ilmentyä, kun yksi ohjelma uudelleen käyttää koodia toisesta.

Lähteet

muokkaa
  1. Päivi Hietanen: C++ ja olio-ohjelmointi, s. 8. Teknolit, 2000. ISBN 951-846-000-0
  2. a b c d e f g h i Asikainen Eetu, Vanhala Erno: LUT-ohjelmoinnin-perusteet-ohjelmointiopas github.com. 19.1.2023. Viitattu 11.3.2023.
  3. Controlling Access to Members of a Class (The Java™ Tutorials > Learning the Java Language > Classes and Objects) docs.oracle.com. Viitattu 31.3.2023.
  4. Mordechai Ben-Ari: Compile and runtime errors in Java (2.2 Compile-time error identifiers) introcs.cs.princeton.edu. 24.01.2022. Viitattu 31.03.2022.
  NODES
Done 1
see 10