Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä



Samankaltaiset tiedostot
Ohjelmoinnin jatkokurssi, kurssikoe

Tenttikysymykset. + UML-kaavioiden mallintamistehtävät

Metodien tekeminen Javalla

Mikä yhteyssuhde on?

Rajapinta (interface)

Java kahdessa tunnissa. Jyry Suvilehto

2. Olio-ohjelmoinista lyhyesti 2.1

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Rajapinnat ja sisäluokat

1 Tehtävän kuvaus ja analysointi

T Olio-ohjelmointi Osa 5: Periytyminen ja polymorfismi Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

Aalto Yliopisto T Informaatioverkostot: Studio 1. Oliot ja luokat Javaohjelmoinnissa

Tehtävä 1. Tehtävä 2. Arvosteluperusteet Koherentti selitys Koherentti esimerkki

Ohjelmistojen mallintaminen. Luento 10, 3.12.

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Luokan sisällä on lista

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Olioiden yhteistyön mallintaminen

4. Luokan testaus ja käyttö olion kautta 4.1

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2

Sisällys. JAVA-OHJELMOINTI Osa 7: Abstrakti luokka ja rajapinta. Abstraktin luokan idea. Abstrakti luokka ja metodi. Esimerkki

Olio-ohjelmointi Javalla

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

Kompositio. Mikä komposition on? Kompositio vs. yhteyssuhde Kompositio Javalla Konstruktorit set-ja get-metodit tostring-metodi Pääohjelma

private TreeMap<String, Opiskelija> nimella; private TreeMap<String, Opiskelija> numerolla;

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

18. Abstraktit tietotyypit 18.1

Ohjelmointi 2 / 2010 Välikoe / 26.3

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

A) on käytännöllinen ohjelmointitekniikka. = laajennetaan aikaisemmin tehtyjä luokkia (uudelleenkäytettävyys)

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

Rajapinnasta ei voida muodostaa olioita. Voidaan käyttää tunnuksen tyyppinä. Rajapinta on kuitenkin abstraktia luokkaa selvästi abstraktimpi tyyppi.

Olio-ohjelmoinnissa luokat voidaan järjestää siten, että ne pystyvät jakamaan yhteisiä tietoja ja aliohjelmia.

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. IX Suunnittelumallit Proxy, Factory Method, Prototype ja Singleton

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Pakkaukset ja määreet

Kertaus: yleistys-erikoistus ja perintä

Sisällys. 11. Rajapinnat. Johdanto. Johdanto

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3

Java-API, rajapinnat, poikkeukset, UML,...

7/20: Paketti kasassa ensimmäistä kertaa

Taulukot. Jukka Harju, Jukka Juslin

Tenttikysymykset. + UML- kaavioiden mallintamistehtävät

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

Yksikkötestaus. import org.junit.test; public class LaskinTest public void testlaskimenluonti() { Laskin laskin = new Laskin(); } }

Ohjelmistojen mallintaminen Olioiden yhteistyö Harri Laine 1

Javan perusteita. Janne Käki

9. Periytyminen Javassa 9.1

Tämän lisäksi listataan ranskalaisin viivoin järjestelmän tarjoama toiminnallisuus:

Hakemistojen sisällöt säilötään linkitetyille listalle.

Osio 4: Graafinen käyttöliittymä

9. Periytyminen Javassa 9.1

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Listarakenne (ArrayList-luokka)

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Luokat ja oliot. Ville Sundberg

T Henkilökohtainen harjoitus: FASTAXON

Sisällys. 15. Lohkot. Lohkot. Lohkot

12. Näppäimistöltä lukeminen 12.1

1. Omat operaatiot 1.1

Linkitetystä listasta perittyä omaa listaa käytetään muun muassa viestiin liittyvien vastausten säilömiseen.

4. Olio-ohjelmoinista lyhyesti 4.1

Ohjelmistojen mallintamisen ja tietokantojen perusteiden yhteys

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

16. Javan omat luokat 16.1

YHTEYSSUHDE (assosiation)

8. Näppäimistöltä lukeminen 8.1

Ohjelmoinnin perusteet Y Python

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

SEPA REFAKTOROINTI Antti Ahvenlampi, 57408L Erik Hakala, 57509T

Ohjelmistotuotanto. Luento

Ohjelmistojen mallintaminen, oliosuunnittelua ja suunnittelumalleja

Luokkamalli LUOKKAKAAVIO. Tämän osan sisältö. Luokkamalli. Luokka ja olio. Luokkakaavio (class diagram)

TyontekijaOhjelma. +main() (käyttää) + = public - = private

Ohjelmistojen mallintaminen viikon 4 laskareiden mallivastauksia

Oliot viestivät metodeja kutsuen

Liite 1. Projektin tulokset (Semaforit Javassa) Jukka Hyvärinen Aleksanteri Aaltonen

Sisällys. JAVA-OHJELMOINTI Osa 6: Periytyminen ja näkyvyys. Luokkahierarkia. Periytyminen (inheritance)

7. Näytölle tulostaminen 7.1

Ohjelmistojen mallintaminen. Luento 4,

812341A Olio-ohjelmointi, IX Olioiden välisistä yhteyksistä

JUnit ja EasyMock (TilaustenKäsittely)

Ohjelmoinnin perusteet Y Python

7. Oliot ja viitteet 7.1

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

Ohjelmoinnin peruskurssien laaja oppimäärä

Luokka Murtoluku uudelleen. Kirjoitetaan luokka Murtoluku uudelleen niin, että murtolukujen sieventäminen on mahdollista.

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 16.3

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmistotuotanto. Luento

Ohjelmistojen mallintaminen, sekvenssikaaviot

Ohjelmistotekniikan menetelmät, koe

Ohjelmoinnin perusteet Y Python

5. HelloWorld-ohjelma 5.1

Ohjelmistojen mallintaminen. Luento 7,

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Transkriptio:

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä Matti Luukkainen 10.12.2009 Tässä esitetty esimerkki on mukaelma ja lyhennelmä Robert Martinin kirjasta Agile and Iterative Development löytyvästä esimerkistä. Martinin esimerkki on huomattavasti laajempi, kattaen kirjasta peräti 103 sivua. Esimerkki ei varsinaisesti kuulu syksyn 09 OhMa-kurssille, mutta on erittäin hyvä havainnollistus muutamasta oliosuunnitteluperiaatteesta ja näinollen hyvää oppia elämää varten. Esimerkki tulee todennäköisesti OhMa-monisteen syksyn 2010 versioon. Esimerkki on kirjoitettu suhteellisen nopeasti, joten tekstissä on varmasti kirjoitusvirheitä. Pahoittelen virheistä ja tekstin lukeminen tapahtukoon lukijan omalla vastuulla. Varmuudella kirjoitusvirheettömiä ovat ainoastaan tyhjät paperit. Yrityksen työntekijä (kuvataan luokkana Henkilö) voi saada joko kuukausi- tunti- tai provisiopalkkaa. Päätetään mallintaa tilanne liittämällä kuhunkin Henkilö-olioon Palkkausrajapinnan toteuttava olio (käytetty luennolla 7 esiteltyä ideaa erottaa henkilö ja henkilön rooli, tässä tapauksessa palkkausrooli ): Jokainen palkkaustyyppi siis osaa laskea palkan, eli käytännössä palauttaa palkkana olevan euromäärän. Tuntipalkkaan liittyy Tuntikirjauksia, eli päivämäärän ja työtuntien yhdistelmiä. Provisiopalkkaan taas liittyy Myyntikirjauksia, eli myyntisumman, provisioprosentin ja päivämäärän yhdisteitä. Provisiopalkatulla henkilöllä palkka muodostuu myyntisummasta kerrottuna provisioprosentilla. Provisioprosentti voi olla erilainen eri myyntien yhteydessä.

Metodien laske() toteutus on aika ilmeinen: class Kuukausipalkka implements Palkkaus{ double palkka; double laske(){ return palkka; class Tuntipalkka implements Palkkaus { double tuntipalkka; ArrayList<Tuntikirjaus> tunnit; double laske(){ double p = 0; for ( Tuntikirjaus t : tunnit ){ p += t.gettunnit() * tuntipalkka; return p; class Provisiopalkka implements Palkkaus { ArrayList<Myyntikirjaus> myynnit; double laske(){ double p = 0; for ( Myyntikirjaus m : myynnit){ p += m.getprosentti() * m.getmyynti(); return p; Henkilö-olio tietää palkkansa kysymällä sitä omalta Palkkaus-rajapinnan toteuttamalta palkkauksen yksityiskohdista vastaavalta oliolta, eli luokan Henkilö-metodi haepalkka: class Henkilö{ Palkkaus palkkaus; double haepalkka(){ double palkka = palkkaus.laske(); return palkka; Palkkaus haepalkanmaksu(){ retutn palkkaus; Koodista huomaamme, että Henkilö-luokalla on myös metodi haepalkanmaksu(). Metodi palauttaa viitteen Henkilö-olioon liittyvään Palkkaus-rajapinnan toteuttavaan olioon. Metodin käyttötarkoitus selviää myöhemmin.

Järjestelmässä on yksi luokan Työntekijät olio, jonka tehtävänä on tuntea ja kaikki yksittäiset Henkilö-oliot. Luokalla on metodi Henkilö haehenkilö(string nimi) jonka avulla saadaan selville viite nimellä haettuun henkilöön, sekä metodi ArrayList<Henkilö> haekaikki(); joka palauttaa viitteet kaikkiin Henkilö-olioihin listana. Käyttöliittymä (seuraavalla sivulla olevassa kuvassa pakkaus UI) on eristetty täysin edellä esitetyistä sovelluslogiikan olioista (eli Henkilöistä ja niihin liittyvistä Palkkaus-rajapinnan toteuttavista oliosta). Eristäminen on toteutettu käyttämällä ns. komentoperiaatetta (engl. command pattern), joka on yksi hyvin tunnettu suunnittelumalli. Komentoperiaatteen idea on melko samantapainen kuin luennoilla ja monisteessa esitelty ohjausperiaate, eli periaate, jonka mukaan jokaisella (tai ainakin osalla) käyttötapauksista on oma luokkansa, jonka oliot huolehtivat käyttötapauksen toiminnallisuuden aikaansaamisesta sovelluslogiikan olioiden yhteistyönä. Ohjausolioissa ja komento-oliossa on kuitenkin muutamia huomattavia eroja kuten kohta käy ilmi. Komentoperiaatteessa on olemassa abstrakti luokka Komento, joka määrittelee ainoastaan yhden abstraktin metodin do(). Metodilla ei ole mitään parametreja. Jos on tarvetta kumota komentoja, voidaan luokkaan myös määritellä metodi undo(), jälleen ilman parametreja. Jokainen erityinen komento määritellään abstraktin luokan Komento perivänä luokkana, joista jokainen toteuttaa metodin do() omalla tavallaan. Toteutetaan nyt komennot seuraaviin toiminnallisuuksiin (joita voi itseasiassa ajatella järjestelmän käyttötapauksina): maksa kaikille palkka lisää työntekijälle työtunteja lisää työntekijälle myyntisumma ja siihen liittyvä provisioprosentti Jokaista edellistä vastaavat luokat siis määritellään perimään Komento. Luokkakaavio seuraavassa:

Näistä luokka MaksaKaikille tuntee rajapinnan Pankki, jonka avulla se pystyy tekemään maksusuorituksia työntekijöiden tileille. Rajapinta Pankki on oikeastaan fasadi, jonka taakse on piilotettu kaikki se koodi, joka ottaa yhteyttä pankin tietojärjestelmään. Käyttöliittymän kannalta toiminnallisuuden suorittaminen on erittäin helppoa. Täytyy ainoastaan luoda sopiva Komento-luokan aliluokan olio ja kutsua sen metodia do(). Mistä komennon aliluokat saavat syötteensä? Nythän ainoalla metodilla do ei ole mitään parametreja! Syöte tulee konstruktorin parametreina. Ideana on, että jokaista toimenpidettä varten luodaan oma komennon aliluokan olio, jolle sitten kutsutaan do(). Luotaessa komennon aliluokan olioa, syöte annetaan konstruktorin parametreina. Seuraavassa Javana se, mitä (joku) käyttöliittymäolio tekee kunkin toiminnallisuuden yhteydessä: Suoritetaan palkanmaksu: Komento k = new MaksaKaikille(); k.do(); Lisätään työntekijälle työtunteja: Oletetaan tässä, että on olemassa käyttöliittymäolio nimeltään lomake, jolta syötetiedot kysellään. String nimi = lomake.getnimi(); int tunnit = lomake.gettunnit(); Date päiväys = lomake.getpaiva(); Komento k = new LisääTunti(nimi, tunnit, päiväys); k.do();

Lisätään työntekijälle myyntisumma ja siihen liittyvä provisioprosentti: String nimi = lomake.getnimi(); double myynti= lomake.getmyynti(); double pros = lomake.getprosentti(); Date päiväys = lomake.getpaiva(); Komento k = new LisääTunti(nimi, myynti, pros, päiväys); k.do(); Huomaamme, että käyttöliittymän kannalta asia on todella yksinkertainen! Tässäkin olisi vielä mahdollisuus yksinkertaistamiseen, jos käytettäisiin ns. tehdasperiaatetta (engl. factory pattern). Tällöin käyttöliittymän ei edes tarvitsisi tietää Komennon aliluokista mitään, se ainoastaan pyytäisi oliotehtaalta sopivia Komento-oliota. Seuraavassa luokkakaavio, jossa koottuna kaikki edellinen: Kaikki monimutkaisuus on siis eristynyt Komento-luokan aliluokkien sisälle. Suunnittelemme seuraavaksi mitä kukin erillinen komento tekee. Kaikki Komento-luokan aliluokkien oliot ovat siis jonkin käyttöliittymäolion luomia ja käyttöliittymäolio kutsuu Komentojen do()-metodia. MaksaKaikille Ensin pyydetään Työntekijät-luokalta lista työntekijöistä. Sen jälkeen kysytään jokaisen työntekijän palkkaa ja tilinumeroa ja maksetaan palkka pankin avulla. Sekvenssikaavio seuraavassa. Huomionarvoista on, että Henkilö-oliot eivät tiedä itse palkkaansa, vaan aivan kuten aiemmin jo mainittiin, kutsuttaessa Henkilön metodia haepalkka(), kysyy se palkkansa omalta Palkkausrajapinnan toteuttamalta olioltaan. Sekvenssikaaviossa tätä ei kuitenkaan ole näytetty.

Koodina: class MaksaKaikille extends Komento { void do(){ ArrayList<Henkilö> henkilöt = tyontekijat.haekaikki(); for ( Henkilö h : henkilöt ){ double palkka = h.haepalkka(); int tilinro = h.haetilinro(); pankki.maksusuoritus( tilinro, palkka ); Huomionarvoista tässä ja seuraavissakin skenaarioissa on se, että yksi Komento-olio on kertakäyttöinen, eli käyttöliittymä luo komento-olion tarvittaessa, suorittaa se metodin do() ja sen jälkeen olioa ei enää käytetä uudelleen. Komennon seuraavaa suorituskertaa varten luodaan aina uusi olio. LisääTunti Ensin haetaan henkilö jolle tunnit lisätään. Kuten aiemmin päätimme, tuntikirjaus liittyy Henkilöoliolla olevalle Palkkaus-rajapinnan toteuttavalle oliolle. Henkilölle on määritelty operaatio haepalkkas(). Operaatio palauttaa Henkilö-olioon liittyvän Palkkaus-rajapinnan toteuttavan olion. Kun henkilö-olio on tiedossa, haetaan Henkilöön liittyvä Palkkaus-rajapinnan toteuttava olio, joka on tuntipalkkaa saavalla henkilöllä luokan Tuntipalkka olio. Luodaan uusi Tuntikirjaus-olio, joka lisätään Henkilön Palkkaus-rajapinnan toteuttavalle oliolle. Sekvenssikaaviona:

Koodina: Luokalla Henkilö on siis operaatio haepalkkaus: class Henkilö{ Palkkaus palkkaus; Palkkaus haepalkkaus(){ return palkkaus; /* */ Eli Henkilö-oliolta saadaan tarvittaessa viite siihen liittyvään Palkkaus-rajapinnan toteuttavaan olioon. Nyt lisätään tuntipalkatulle henkilölle työtunteja, ja jotta tunnit voidaan lisätä, on haepalkkaus()-metodin palauttama olio muunnettava todelliseen tyyppiinsä, eli TuntiPalkkausolioksi. Näin tapahtuu seuraavassa koodissa metodin do() toisella rivillä. class LisääTunnit extends Komento { String nimi; int tunnit; Date päiväys; LisaaTunnit(Stiring n, int t, Date p){ nimi = n; tunnit = t; päiväys = p; void do(){ Henkilö h = työntekijat.haehenkilö(nimi); Tuntipalkka tp = (Tuntipalkka) h.haepalkkaus(); Tuntikirjaus tk = new Tuntikirjaus(tunnit, päiväys); tp.lisää(tk);

LisääMyynti Luokan toteutus on lähes samanlainen kuin luokan LisääTunnit. Esitetään seuraavassa ainoastaan koodi. Koska nyt kyseessä provisiopalkattu henkilö, muunnetaan haepalkkaus()-metodin palauttama olio tyyppiin Myyntikirjaus. class LisääMyynti extends Komento { String nimi; double myynti; double prosentti; Date päiväys; LisaaMyynti(Stiring n, double m, double pr, Date pv){ nimi = n; myynti = m; prosentti = pr; päiväys = pv; void do(){ Henkilö h = työntekijat.haehenkilö(nimi); Provisiopalkka pp = (Provisiopalkka) h.haepalkkaus(); Myyntikirjaus mk = new Myyntikirjaus(myynti, prosentti, päiväys); pp.lisää(mk); Huomioita Yksi tämän esimerkin motivaatioita oli näyttää, miten, ns. role-player-periaatteen mukaan usealla oliolla (Henkilö-olio ja siihen liittyvä Palkkaus-rajapinnan toteuttama olio) esitetyn työntekijän käsittely tapahtuu suunnittelutasolla. Toiminnon MaksaKaikille yhteydessä siis kutsuttiin Henkilön metodia haepalkka(). Tämän suorituksen Henkilö delegoi omalle Palkkaus-rajapinnan toteuttamalle oliolleen. Toimintojen LisääTunti ja LisääMyynti kohdalla tilanne on hiukan monimutkaisempi. Näissä toiminnoissa on lisättävä olio (Tuntikirjaus ja Myyntikirjaus) Palkkaus-rajapinnan toteuttaman olion alle. Asia on hoidettu siten, että Komento-olio pyytää Henkilöltä sen Palkkaus-rajapinnan toteuttaman olion (TuntiPalkkaus tai Provisiopalkkaus) ja kutsuu sitten tälle metodia, jonka avulla kirjaus lisätään olion alle. Kumpi on suositeltavampi ohjausperiaate vai komento-olioiden käyttö? Riippuu tilanteesta. Komento-oliot voivat olla erittäin käytännöllisiä tilanteessa, jossa halutaan mahdollistaa myös komentojen undo()-operaatio. Tällöin voidaan muistaa jo suoritetut komennot, ja tarvittaessa voidaan näille helposti suorittaa undo()-metodi. Esim. piirto-ohjelma kannattaisi toteuttaa tekemällä jokaisesta piirtotoimenpiteestä oma Komento-olio, jolle on do()-metodin lisäksi toteutettu myös undo() joka kumoaa piirtotoimenpiteen aikaansaamat vaikutukset. Pitämällä sitten kirjaa suoritetuista Komento-oliosta, voitaisiin melko helposti toteuttaa piirto-ohjelmalle undotoiminnallisuus.

Komento-oliot sopivat yleensä ainoastaan rajallisen kokoisten operaatioiden suorittamiseen. Jos ajatellaan esim. lainaustapahtumaa kirjastosta, oitaisiin määritellä käyttötapaus, joka sisältää lainaajan tunnistamisen sekä useiden kirjojen lainaamisen. Tällaiselle käyttötapaukselle olisi suhteellisen helppo tehdä käyttötapauskohtainen ohjausolio. Komento-olion kaikki syötedata annetaan konstruktorin parametrina, joten jos dataa on paljon ja datan määrä ei ole ennalta tiedossa (useiden kirjojen lainaaminen) ei komento-olio sovi kunnolla tarkoitukseen. On myös mahdollista käyttää molempia. Eli käytetään käyttötapauskohtaisia ohjausoliota, jotka sitten kutsuvat sopivia komento-oliota.