410 likes | 786 Views
Java-ohjelmointi Opas ammattimaiseen osaamiseen Oliosuunnittelu ja luokkakohtaiset komponentit.
E N D
Java-ohjelmointiOpas ammattimaiseen osaamiseenOliosuunnittelu ja luokkakohtaiset komponentit
© Jukka Harju Nämä kalvot on lisensoitu Creative Commons Attribution-ShareAlike 1.0 -lisenssillä. Lisäys edelliseen lisenssiin: Kalvojen muokkaaminen on sallittu vain opettajille, joiden kursseilla käytetään kurssikirjana Tuloksellinen Java-ohjelmointi - tai Java ohjelmointi opas ammattimaiseen osaamiseen -kirjaa.
© Jukka Harju Sisällys Ohjelmistosuunnittelusta Tarvittavien luokkien tunnistaminen Luokka-attribuutit ja -metodit Metodien suunnittelu
© Jukka Harju Ohjelmistosuunnittelu • Ohjelmistosuunnittelussa käytetään useita eri prosessimalleja (mm. Rational Unified Process ja eXtreme Programming) • Käytettävästä mallista riippumatta ohjelmistoprosessi sisältää lähes aina seuraavat osat: • Vaatimusmäärittely • Toiminnallinen määrittely • Tekninen suunnittelu • Toteutus • Testaus • Tuotantoonsiirto • Ylläpito
© Jukka Harju Vaatimusmäärittely • Vaatimusmäärittelyssä kuvataan ohjelmistolle asetettavat vaatimukset eli kuinka ohjelmiston tulee toimia. • Vaatimukset saadaan ohjelmiston tilaajalta, joka suomalaisessa ohjelmistoteollisuudessa on useimmiten asiakkaana oleva yritys. • Vaatimusmäärittely on korkean tason kuvaus, jota tarkennetaan alemman tason kuvauksissa (mm. toiminnallinen määrittelykuvaus ja suunnittelukuvaus). • Esimerkki suorituskykyyn liittyvästä vaatimuksesta web-pohjaiselle järjestelmälle: • Järjestelmän tulee pystyä palvelemaan vähintään 1000 yhtäaikaista käyttäjää alle 2 sekunnin vasteajoilla.
© Jukka Harju Toiminnallinen määrittely • Toiminnallisessa määrittelyssä kuvataan kaikki järjestelmän toteuttamat toiminnot ja liitännät järjestelmän ulkopuolelle. • Toiminnallisen määrittelyn tuloksena syntyy määrittelydokumentti, joka kuvaa mitä järjestelmällä voi tehdä ja miten käyttäjä voi järjestelmää käyttää. • Toiminnallinen määrittely ei ota kantaa siihen miten toiminnot tulee toteuttaa. • Määrittelyn tulisi olla niin kattava, että seuraavissa vaiheissa ei ole enää epäselvää miten ohjelman tulee toimia eri tilanteissa. Tähän ei kuitenkaan käytännössä koskaan päästä. • Toiminnallisessa määrittelyssä voidaan kuvata esimerkiksi web-käyttöliittymän eri näytöt ja näytöillä olevat komponentit. Määrittelydokumentin tulee sisältää tällöin sekä sanalliset että graafiset kuvaukset ko näytöistä.
© Jukka Harju Tekninen suunnittelu • Teknisen suunnittelu tarkoituksena on kuvata ohjelmiston tekninen arkkitehtuuri. • Suunnittelussa kuvataan kuinka ohjelma suorittaa toiminnallisessa määrittelyssä kuvatut toiminnot. • Suunnittelukuvauksesta ilmenee kuinka ongelma voidaan jakaa pienempiin, hallittavan kokoisiin osiin ja mitä kukin osa tekee. • Olio-orientoinut suunnittelukuvaus kertoo mitä luokkia ja olioita tarvitaan ja mitkä ovat niiden keskinäiset suhteet. • Sisältää mm. käytettyjen ohjelmistokomponenttien (luokkien) kuvaukset, niiden keskinäisten riippuvuuksien kuvaukset, tietovaraston kuvauksen jne. • Kuvaus koostuu eri tasoista, esim. ohjelmataso, luokkataso, metoditaso. • Kaikkein alimman tason osat kuvaukseen saa tuotettua esim. javadocista.
© Jukka Harju Toteutus • Toteutuksessa toteutetaan suunnittelukuvauksen mukainen lähdekoodi. • Aloittelevat ohjelmoijat luulevat usein, että toteutus on ohjelmistoprosessin ydin, mutta itse asiassa sen tulisi olla vähiten luovuutta vaativa kohta. • Lähes kaikki tärkeät päätökset tehdään jo vaatimusmäärittelyn ja suunnittelun aikana. • Toteutuksen tulisi keskittyä ohjelmoinnin yksityiskohtiin, tyyliin (JCC) ja dokumentointiin (javadoc). • Tuloksena on suoritettava ohjelmisto, joka yleensä kuitenkin sisältää suhteellisen paljon toiminnallisia virheitä.
© Jukka Harju Testaus • Testauksessa yritetään varmistaa, että ohjelma täyttää sille asetetut vaatimukset. • Testauksessa yritetään löytää virheitä. Koko ohjelma tulee aina testata huolella. • Testaus ei todista ohjelman toimivan oikein vaan todistetuksi tulee ainoastaan se, että ohjelma sisältää virheitä. • Virheitä löytyy toteutuksen huolellisuudesta riippumatta lähes kaikista ohjelmistoista. • Debuggaus on löydettyjen virheiden alkuperän etsimistä ja virheiden korjaamista.
© Jukka Harju Tuotantoonsiirto • Tuotantoonsiirrossa testattu ja toimivaksi todettu ohjelmisto asennetaan toimintaan tuotantoympäristöön. • Tuotantoonsiirtoon liittyy usein mm. laitteiston asennus ja valmistelu, tietoliikenneyhteyksien valmistelu ja käyttäjien kouluttaminen.
© Jukka Harju Ylläpito • Ylläpito sisältää toimenpiteet, joita ohjelmistolle tarvitsee tuotantoonsiirron jälkeen tehdä, jotta asiakas olisi tyytyväinen ohjelmistoon. • Laadukkaastikin ohjelmistosta löytyy useimmiten tuotantoonsiirron jälkeenkin pieniä virheitä, jotka tulee korjata. • Käyttäjille tulee uusia tarpeita, joten ohjelmistoon on lisättävä uusia ominaisuuksia.
© Jukka Harju Sisällys Ohjelmistosuunnittelusta Tarvittavien luokkien tunnistaminen Luokka-attribuutit ja -metodit Metodien suunnittelu
© Jukka Harju Tarvittavien luokkien tunnistaminen • Oliosuunnittelun tärkein tehtävä on määritellä ohjelmistossa tarvittavat luokat ja oliot sekä niiden väliset yhteydet. • Luokat voivat olla Javan valmisluokkia, vapaasti saatavia Javan ulkopuolisia valmisluokkia (ns. 3rd party -luokkia), omia aiemmin tehtyjä luokkia tai täysin uusia. • Eräs tapa tunnistaa mahdollisesti tarvittavat luokat on vaatimusmäärittelyn apuna käyttäminen. • Oliot esiintyvät vaatimusmäärittelyssä useimmiten substantiiveina ja metodit verbeinä.
© Jukka Harju Tarvittavien luokkien tunnistaminen • Osa vaatimusmäärittelystä: • Ohjelman tulee tuottaa asiakkaalle lähetettävä lasku. • Lasku tulee kirjoittaa tekstitiedostoon. • Lasku sisältää: • Myyjän nimen ja osoitteen • laskutuspäivän • Asiakkaan nimen ja osoitteen • Laskun identifioivan laskunumeron • Viitenumeron • Tilinumeron, jolle lasku maksetaan • Laskun muodostamisessa käytetyn tuntihinnan • … • Kaikki virheilmoitukset kirjoitetaan lokitiedostoon. • Kaikista substantiiveista ei kuitenkaan tule luokkaa / oliota lopullisessa ratkaisussa!
© Jukka Harju Tarvittavien luokkien tunnistaminen • Luokka edustaa joukkoa samankaltaisia olioita. • Luokat tulisi nimetä yksikkömuodossa olevilla substantiiveilla: • Oppilas • Osoite • Viitenumero • Poikkeuksiakin tietysti löytyy (esim. Tiedostotyokalut).
© Jukka Harju Tarvittavien luokkien tunnistaminen • Joskus on vaikea päättää tarvitaanko asian esittämiseen erillinen luokka. • Esimerkiksi asiakkaan osoite voidaan esittää useampana erillisenä attribuuttina tai yhtenä Osoite-tyyppisenä attribuuttina. • Pääsääntönä on välttää kovin monimutkaisten luokkien tekemistä. Tällaiset luokat tulee pilkkoa useampaan pienempään luokkaan.
© Jukka Harju Tarvittavien luokkien tunnistaminen • Luokissa halutaan esittää tarvittava määrä yksityiskohtia. • Esimerkiksi jokaista kulkuneuvoa kuvaamaan ei välttämättä tarvita omaa luokkaa, vaan voidaan käyttää yleisempää Kulkuneuvo-luokkaa tarvittavin attribuutein. • Tällöin Kulkuneuvo-luokan attribuuttina voi olla nimi, joka kertoo mikä laite on kyseessä. • Esimerkiksi luokkien Auto, Moottoripyora ja Polkupyora sijaan saattaisi riittää luokka Kulkuneuvo, jolla voisi olla vaikkapa attribuutit nimi, kayttoonottovuosi ja omistaja.
© Jukka Harju Tarvittavien metodien tunnistaminen • Jokainen ohjelmassa suoritettava toiminto täytyy toteuttaa jonkin luokan metodina. • Metodit nimetään käyttäen verbejä, esimerkiksi haeViitenumero, laskeTarkiste. • Suunnittelun alkuvaiheessa riittää tunnistaa kunkin luokan tärkeimmät tehtävät.
© Jukka Harju Sisällys Ohjelmistosuunnittelusta Tarvittavien luokkien tunnistaminen Luokka-attribuutit ja -metodit Metodien suunnittelu
© Jukka Harju Luokka-attribuutit ja -metodit • Luokkametodia voidaan kutsua ilman yhtäkään oliota suoraan luokan nimen kautta. • Esimerkiksi Javan Math-luokan metodit ovat luokkametodeja: double logaritmi = Math.log(2); • Myös attribuutit voivat olla luokka-attribuutteja, tällainen on esimerkiksi Javan System-luokan out-attribuutti: System.out.println("Kone on kaapattu Kuubaan"); • Suunnittelun eräs vaihe on määritellä tuleeko attribuutin olla oliokohtainen vai luokka-attribuutti.
© Jukka Harju static-avainsana • Luokka-attribuutit ja luokkametodit määritellään static-avainsanaa käyttäen. • static-avainsana määrittelee metodin tai attribuutin luokkakohtaiseksi (ei oliokohtaiseksi). Esimerkiksi. //luokka-attibuuttiprivate static double tuntilaskutus; //luokkametodipublic static double laskeSumma(int a, int b) { return a + b;}
© Jukka Harju Luokka-attribuutti • Yleensä jokainen olio attribuutteineen sijaitsee omalla muistialueellaan. • Luokka-attribuutista on kuitenkin olemassa vain yksi kopio, joka on yhteinen kaikille olioille. • Luokka-attribuutti luodaan kun luokkaan viitataan ensimmäisen kerran. • Luokka-attribuutin arvon muutos näkyy kaikille ko. luokan olioille.
© Jukka Harju Luokka-attribuutti • Luokka-attribuutti ei tule alustaa konstruktorissa, sillä tällöin luokka-attribuutille asetetaan aina oletusarvo uuden olion luonnin yhteydessä. • Luokka-attribuutti voidaan alustaa staattisessa alustuslohkossa. • Staattinen alustuslohko sijoitetaan luokassa heti attribuuttien jälkeen. Esimerkki. private static final double TUNTILASKUTUS; static { TUNTILASKUTUS = 125.0; } • Luokka-attribuutteja käsittelevien get- ja set-metodien tulee olla luokkametodeja.
© Jukka Harju Luokkametodi public class Laskin { public static long korotaPotenssiin(int kantaluku, int eksponentti) { long tulos = kantaluku; for(int i=0; i < eksponentti; i++) { tulos = tulos * kantaluku; } return tulos; } } • Koska metodi potenssi on luokkametodi, voidaan sitä kutsua seuraavasti (vrt. Math-luokka): • long tulos = Laskin.korotaPotenssiin(5,3);
© Jukka Harju Luokka-attribuutit ja -metodit • main-metodi on aina luokkametodi (ns. staattinen metodi). Java-kääntäjä kutsuu sitä luomatta yhtään oliota. • Luokkametodeista ei voida viitata oliokohtaisiin attribuutteihin (ilmentymäattribuutteihin), koska käytettävissä ei ole yhtään olioviittausta (luokkametodia ei kutsuta viittausmuuttujan kautta). • Luokkametodista voidaan kuitenkin käyttää luokka-attribuutteja ja paikallisia muuttujia. • Koodissa luokka-attribuutit kirjoitetaan aina ennen ilmentymäattribuutteja.
© Jukka Harju Luokka-attribuutit ja -metodit import java.io.File; import java.io.IOException; import java.util.Scanner; import java.util.logging.Level; /** * Laskutus-sovelluksen viitenumerokäsittelymetodit * sisältävä luokkakirjasto. * @author Jukka Juslin, Jukka Harju */ public class Viitenumero { /** * Palauttaa seuraavan käyttämättömän viitenumeron ja * päivittää sen tiedostoon viite.txt. * @return viitenumero */ public static String haeViitenumero() { //selvitetään seuraava käyttämätön viitenumero long seuraava; seuraava = viimeinenKaytettyViitenumero() + 1; //päivitetään viitenumero takaisiin tiedostoon try { TiedostoTyokalut.kirjoita( String.valueOf(seuraava), "viite.txt"); } catch (IOException ex) { TiedostoTyokalut.logger.log( Level.SEVERE, "Viimeisintä viitenumeroa ei pystytty " + "päivittämään viite.txt-tiedostoon"); } int eheys = laskeTarkiste(seuraava + ""); String viitenumero = seuraava + "" + eheys; return viitenumero; } • Kirjan Laskutus-ohjelma sisältää Viitenumero-luokan, jossa on luokkametodi haeViitenumero. • Metodi hakee tiedostosta viimeksi käytetyn perusviitenumeron ja selvittää seuraavan käyttämättömän. Metodi myös päivittää viimeisimmän käytetyn viitenumeron. • Metodin käyttää apuna saman luokan luokkametodeja viimeinenKaytettyViitenumero ja laskeTarkiste sekä TiedostoTyokalut-luokan luokka-metodia kirjoita.
© Jukka Harju this-avainsana • this-avainsana viittaa olioon itseensä. • Metodin sisällä käytettynä this-avainsana siis viittaa olioon, jolle metodi suoritetaan. • Esimerkiksi kutsuttaessa oliolle metodia setEtunimi: oppilas1.setEtunimi("Anna"); oppilas2.setEtunimi("Jussi"); • Ensimmäisessä kutsussa setEtunimi-metodissa käytetty this-avainsana viittaa samaan olioon kuin oppilas1-viittausmuuttuja, toisessa samaan olioon kuin oppilas2-viittausmuuttuja. • this-avainsana ei ole käytettävissä luokkametodeissa, miksi?
© Jukka Harju this-avainsana • this-avainsanalla voidaan erottaa attribuutti ja samanniminen paikallinen muuttuja toisistaan. • Esimerkiksi setEtunimi-metodissa: public void setEtunimi(String etunimi) { if(etunimi != null && etunimi.length() > 0) { this.etunimi = etunimi; } else { System.out.println("Etunimi ei saa olla tyhjä!"); } }
© Jukka Harju Sisällys Ohjelmistosuunnittelusta Tarvittavien luokkien tunnistaminen Luokka-attribuutit ja -metodit Metodien suunnitteleminen
© Jukka Harju Metodien suunnitteleminen • J. G. Brookshear: "Algoritmi on äärellinen joukko täsmällisiä, suoritettavissa olevia ohjeita, jotka ohjaavat päättyvää tehtävän suoritusta." • Esimerkiksi viitenumeron tarkisteen laskeminen: • Viitenumerossa on aina tarkiste. Se lasketaan seuraavasti: • Perusviitetiedoksi valitun luvun (esim. asiakas- tai laskunumeron) numerot kerrotaan oikealta vasemmalle painoilla 7, 3, 1, 7, 3,1 …. • Tulot lasketaan yhteen, ja summa vähennetään seuraavasta täydestä kymmenestä. Erotus on tarkiste, joka merkitään viitenumeron viimeiseksi numeroksi. Jos erotus on 10, tarkiste on 0. (http://www.pankkiyhdistys.fi/sisalto/upload/pdf/viitenumero.pdf) • Jokainen metodi toteuttaa jonkin algoritmin. Algoritmin ei tarvitse olla "monimutkainen". • Algoritmi voidaan kirjoittaa suunnitteluvaiheessa pseudokoodina, joka on yhdistelmä Java-lauseita ja suomea. • Algoritmi voidaan kuvata myös UML-toimintokaaviona.
© Jukka Harju Metodin osittaminen • Metodin tulee olla suhteellisen lyhyt looginen kokonaisuus. Näin koodi säilyy helpompana lukea. • Pitkä metodi tulee pilkkoa useaan pienempään metodiin. • public-tyyppinen metodi voi kutsua private-tyyppisiä (apu)metodeja toiminnon suorittamiseksi. • Apumetodit voivat käyttää edelleen toisia apumetodeja.
© Jukka Harju Metodin osittaminen • Tarkastellaan esimerkkinä kirjan Laskutus-ohjelman Viitenumero-luokan metodia haeViitenumero. • Metodi toteutus sisältää seuraavat toiminnot: • viimeisimmän käytetyn viitenumeron perusviitteen hakeminen tiedostosta • haetun arvon kasvattaminen yhdellä • kasvatetun perusviitteen kirjoittaminen takaisin tiedostoon • tarkisteen laskeminen perusviitteelle • perusviitteen ja tarkisteen yhdistäminen • tuloksen palauttaminen
© Jukka Harju Metodin osittaminen • Kokonaisuudessaan seuraavan käyttämättömän viitenumeron muodostaminen on liian laaja toimenpide suoritettavaksi yhdessä metodissa. Etsitään sopiva tapa jakaa ratkaisu osiin: • toteutetaan viimeisimmän käytetyn perusviitteen hakeminen suojatussa viimeinenKaytettyViitenumero-metodissa. • toteutetaan perusviitteen päivitys tiedostoon TiedostoTyokalut-luokan metodin kirjoita-avulla. • Toteutetaan tarkisteen laskenta suojatussa laskeTarkiste-metodissa.
© Jukka Harju Metodin osittaminen • Katso Viitenumero.java (sivu 78) • UML-luokkakaaviossa attribuutin ja metodin näkyvyyden kuvaamisessa käytetään "+"- ja "-"-merkkejä. • Julkisia komponentteja merkitään "+"-merkillä. • Suojattuja komponentteja merkitään "-"-merkillä. • Luokkakohtaisten komponenttien nimet esitetään alleviivattuina.
© Jukka Harju Laskutus-ohjelma: UML-luokkakaavio
© Jukka Harju Kertaus: Olion välittäminen parametrina • Javassa parametrit välitetään aina arvoparametreina. • Todellisena parametrina annettu (muuttujan) arvo kopioidaan metodille määritellyn paikallisen parametrimuuttujan alkuarvoksi. • Parametrien välityksessä tapahtuu siis sijoittamista. • Kun viittaus olioon välitetään parametrina, viittaavat sekä alkuperäinen viittausmuuttuja, että parametrimuuttuja samaan olioon.
© Jukka Harju Kertaus: Olion välittäminen parametrina • Kun olio välitetään parametrina, näkyy kutsutussa metodissa olion tietosisältöön tehty muutos kutsuneessa metodissa ilman paluuarvon välittämistä. • Alkeistietotyyppisille muuttujille tehdyt muutokset eivät näy kutsuneelle metodille, vaan tällöin on aina käytettävä paluuarvon välittämistä.
© Jukka Harju Metodin kuormittaminen • Metodin kuormittaminen tarkoittaa saman metodinimen käyttämistä useammassa kuin yhdessä metodin määrittelyssä. • Kuormitetun metodin tapauksessa pelkkä metodin nimi ei riitä kertomaan mitä metodia halutaan kutsua. • Metodin signatuuri tarkoittaa metodin nimen ja parametrien yhdistelmää. • Jokaisen (myös kuormitetun) metodin signatuurin täytyy olla uniikki. • Kääntäjä päättää parametrien perusteella mitä kuormitetun metodin versiota kutsutaan.
© Jukka Harju Metodin kuormittaminen Kutsu kirjoita(”jotain”, ”tiedosto.txt”); ohjautuu ao metodeista, ensimmäiselle, miksi? public static void kirjoita(String teksti, String tiedosto) { . . . } public static void kirjoita(StringBuffer teksti, String tiedosto) { . . . }
© Jukka Harju Metodin kuormittaminen • Java-APIsta havaitaan, että esimerkiksi String-luokan println-metodi on kuormitettu: println(String s) println(int i) println(double d) jne. • Seuraavilla komennoilla voidaan kutsua näistä kahta eri versiota: System.out.println("Tulostetaan Merkkijono"); System.out.println(summa);
© Jukka Harju Metodin kuormittaminen • Metodin paluuarvo ei kuulu metodin signatuuriin. • Kuormitetut metodit eivät siis voi erota pelkästään paluuarvon tietotyypin osalta. • Myös konstruktoreja voidaan kuormittaa: • Oletuskonstruktori • Erilaiset parametrilliset konstruktorit