Materiaalin copyright © Arto Wikla. Materiaalia saa vapaasti käyttää itseopiskeluun. Muu käyttö vaatii luvan.

Ohjelmoinnin jatkokurssi: harjoitukset s2010: 3/6

(Muutettu viimeksi 12.11.2010, sivu perustettu 10.11.2010.)

Harjoitustehtävien otsikoilla on värikoodaus: Vihreät tehtävät on syytä tehdä joka tapauksessa. Värittämättömiä ei ole ihan pakko tehdä, mutta nekin ovat hyvin hyödyllisiä ja myös vaikuttavat pisteisiin. Keltaiset tehtävät ovat vähän haastavampia. Nekin lasketaan mukaan harjoituspisteitä määrättäessä, mutta ilmankin niitä harjoituksista voi saada maksimipisteet.

Huom: Jokaisen ohjelmatiedoston alkuun on kirjoitettava kommenttina harjoituskerta, tehtävän numero ja tekijän nimi tyyliin:

// 3. harjoitukset, tehtävä 2.1, Oili Opiskelija

Huom: Ohjaajien eli pajamestarien sivulta löytyy hienoja testauksen apuvälineitä tehtävien tekemisen avuksi!

Tuotevarasto

Luvussa III Oliot ja kapselointi, luokka olion mallina keskeinen esimerkki oli luokka Varasto:

Tälle luokalle aletaan nyt tehdä täydennyksiä aliluokkana.

Tuotevarasto, vaihe 1

Luokka Varasto osaa jo hoidella tuotteen määrän käsittelyn. Nyt tuotteelle halutaan lisäksi tuotenimi ja nimen käsittelyvälineet. Luokan voisi tietysti ohjelmoida alusta alkaen uudelleen, mutta miksi ihmeessä? Ohjelmoidaan Tuotevarasto Varaston aliluokaksi!

Toteutetaan ensin pelkkä yksityinen kenttä tuotenimelle, konstruktori ja getteri nimikentälle:

Muista millä tavoin konstruktori voi ensi toimenaan suorittaa yliluokan konstruktorin!

Käyttöesimerkki:

...
Tuotevarasto mehu = new Tuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
System.out.println(mehu);           // saldo = 988.7, vielä tilaa 11.3
...

Tulostus siis:

Juice
saldo = 988.7, vielä tilaa 11.3

Tuotevarasto, vaihe 2

Kuten edellisestä esimerkistä näkee, Tuotevarasto-olion perimä toString() ei tiedä (tietenkään!) mitään tuotteen nimestä. Asialle on tehtävä jotain! Lisätään samalla myös setteri tuotenimelle:

Uuden toString()-metodin voisi toki ohjelmoida käyttäen yliluokalta perittyjä gettereitä, joilla perittyjen, mutta piilossa pidettyjen kenttien arvoja saa käyttöönsä. Koska yliluokkaan on kuitenkin jo ohjelmoitu tarvittava taito varastotilanteen merkkiesityksen tuottamiseen, miksi nähdä vaivaa sen uudelleen ohjelmointiin. Käytä siis hyväksesi perittyä toStringiä. Muista miten korvattua metodia voi kutsua aliluokassa!

Käyttöesimerkki:

...
Tuotevarasto mehu = new Tuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu);           // Juice: saldo = 989.7, vielä tilaa 10.299999999999955
...

Tulostus siis:

Juice
Juice: saldo = 989.7, vielä tilaa 10.299999999999955

Joukko-operaatioita aliluokassa

Toisen viikon tehtävissä 2.1-2.4 Kokonaislukujoukko-olioille toteutettiin joukko joukko-operaatioita kirjastometodeina. Tehtävissä 3.1-3.4 itse Kokonaislukujoukko-luokkaa täydennettiiin joukko-operaatioaksessorein.

Nyt samat operaatiot toteutetaan vielä kolmannella tavalla: laaditaan Kokonaislukujoukko-luokalle aliluokka KokonaislukujoukkoPlus, joka täydentää perittyjä ominaisuuksia joukko-operaatioilla.

Ota erikoistamisen lähtökohdaksi toisen viikon tehtävän 1.4 "tavallinen" kokonaislukujoukko.

Vihje 1: Paljonkaan uutta ohjelmointia ei taida tulla, koska vastaavat algoritmit olet jo ohjelmoinut...

Vihjei 2: Voit jättää konstruktorin ohjelmoimatta aliluokkaan, koska tässä tapauksessa aliluokkaan syntyy automaattisesti parametriton oletuskonstruktori public KokonaislukujoukkoPlus(), joka myös ihan automaattisesti osaa käydä suorittamassa yliluokan konstruktorin.

Yhdiste

public KokonaislukujoukkoPlus yhdiste(KokonaislukujoukkoPlus toinen) palauttaa arvonaan joukon, joka sisältää kaikki this-joukon ja joukon toinen alkiot.

Käyttöesimerkki:

KokonaislukujoukkoPlus a, b, c;
a = new KokonaislukujoukkoPlus();
b = new KokonaislukujoukkoPlus();
// ....  a:han ja b:hen viedään kaikenlaisia lukuja
c = a.yhdiste(b);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);

Leikkaus

public KokonaislukujoukkoPlus leikkaus(KokonaislukujoukkoPlus toinen) palauttaa arvonaan joukon, joka sisältää täsmälleen kaikki alkiot, jotka kuuluvat sekä this-joukkoon että joukkoon toinen.

Käyttöesimerkki:

KokonaislukujoukkoPlus a, b, c;
a = new KokonaislukujoukkoPlus();
b = new KokonaislukujoukkoPlus();
// ....  a:han ja b:hen viedään kaikenlaisia lukuja
c = a.leikkaus(b);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);

Erotus

public KokonaislukujoukkoPlus erotus(KokonaislukujoukkoPlus toinen) palauttaa arvonaan joukon, joka sisältää kaikki this-joukon alkiot, jotka eivät kuulu joukkoon toinen.

Käyttöesimerkki:

KokonaislukujoukkoPlus a, b, c;
a = new KokonaislukujoukkoPlus();
b = new KokonaislukujoukkoPlus();
// ....  a:han ja b:hen viedään kaikenlaisia lukuja
c = a.erotus(b);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);

Komentotulkki

Muokkaa toisen viikon tehtävän 2.4 komentotulkista sellainen, jossa käytetään KokonaislukuPlus-olioita.

Tuotevarasto ja muutoshistoria

Toisinaan saattaa olla kiinostavaa tietää, millä tavoin jonkin tuotteen varastotilanne muuttuu: onko varasto usein hyvin vajaa, ollaanko usein ylärajalla, onko vaihelu suurta vai pientä, jne. Varustetaanpa siksi Tuotevarasto-luokka taidolla muistaa tuotteen määrän muutoshistoriaa.

Aloitetaan apuvälineen laadinnalla.

Muutoshistoria.java, vaihe 1

Muutoshistorian muistamisen voisi toki toteuttaa suoraankin ArrayList<Double>-oliona luokassa Tuotevarasto, mutta nyt laaditaan kuitenkin oma erikoistettu väline tähän tarkoitukseen. Väline toteutetaan kapseloimalla ArrayList<Double>-olio.

Muutoshistoria-luokan API:

Havainnollista olioiden luontia ja käyttöä pienellä ohjelmalla.

Muutoshistoria.java, vaihe 2

Täydennä Muutoshistoria-luokkaa analyysimetodein:

Pajamestarien vihje: API:n Collections tekee laiskan ohjelmoijan elämän tässä kohtaa helpommaksi...

Luennoijan vihje: Kannattaa muistaa for-each! Se osaa kivasti askeltaa kokoelman kuin kokoelman alkioiden arvot läpi yksi kerrallaan. Tätä kannattaa kokeilla, vaikka laiskottelisinkin pajamestarien vihjeen vinkkaamalla tavalla.

Havainnollista uusien metodien käyttöä ja toimivuutta pienellä ohjelmalla.

Muutoshistoria.java, vaihe 3

Täydennä Muutoshistoria-luokkaa analyysimetodein:

Esimerkki laskennasta: Tarkastellaan esimerkiksi muutoshistoriaa: 0, 5, 9, 2, 6. Muutosten itseisarvot ovat 5, 4, 7 ja 4, joten suurin muutoksen itseisarvo on 7. Varianssi on 12.3 seuraavan perusteella: (1 / 4) * ((0 - 4,4)2 + (5 - 4,4)2 + (9 - 4,4)2 + (2 - 4,4)2 + (6 - 4,4)2) = 12.3

Havainnollista uusien metodien käyttöä ja toimivuutta pienellä ohjelmalla.

MuistavaTuotevarasto, vaihe 1

Toteuta luokan Tuotevarasto aliluokkana MuistavaTuotevarasto. Uusi versio tarjoaa vanhojen lisäksi varastotilanteen muutoshistoriaan liittyviä palveluita. Historiaa hallitaan Muutoshistoria-oliolla.

API-raakile:

Huomaa että tässä esiversiossa historia ei vielä toimi kunnolla; nyt vasta vain aloitussaldo muistetaan.

Käyttöesimerkki:

// tuttuun tapaan:
MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu);           // Juice: saldo = 989.7, vielä tilaa 10.3
...
// mutta vielä historia() ei toimi kunnolla:
System.out.println(mehu.historia()); // [1000.0]
   // saadaan siis vasta konstruktorin asettama historian alkupiste...
...

Tulostus siis:

Juice
Juice: saldo = 989.7, vielä tilaa 10.299999999999955
[1000.0]

MuistavaTuotevarasto, vaihe 2

On aika aloittaa historia! Ensimmäinen versio ei historiasta tiennyt kuin alkupisteen. Täydennä luokkaa metodein

Käyttöesimerkki:

// tuttuun tapaan:
MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu);           // Juice: saldo = 989.7, vielä tilaa 10.3
...
// mutta nyt on historiaakin:
System.out.println(mehu.historia()); // [1000.0, 988.7, 989.7]
...

Tulostus siis:

Juice
Juice: saldo = 989.7, vielä tilaa 10.299999999999955
[1000.0, 988.7, 989.7]

Muista miten korvaava metodi voi käyttää hyväkseen korvattua metodia!

MuistavaTuotevarasto, vaihe 3

Täydennä luokkaa metodilla

Käyttöesimerkki:

MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
mehu.lisaaVarastoon(1.0);
//System.out.println(mehu.historia()); // [1000.0, 988.7, 989.7]

mehu.tulostaAnalyysi();

Metodi tulostaAnalyysi kirjoittaa ilmoituksen tyyliin:

Tuote: Juice
Historia: [1000.0, 988.7, 989.7]
Suurin tuotemäärä: 1000.0
Pienin tuotemäärä: 988.7
Keskiarvo: 992.8

MuistavaTuotevarasto, vaihe 4

Täydennä analyysin tulostus sellaiseksi, että mukana ovat myös muutoshistorian suurin muutos ja historian varianssi.

Havainnollista kehiteltyä analyysiraporttia pienellä esimerkkiohjelmalla.