Ratkaisujen tarkistus

Julkaistu

Ratkaisuja tarkistetaan sekä automaattisesti että opettajien toimesta. Automaattinen tarkistus tapahtuu osin Lausekielinen ohjelmointi I ja II -kurssien tapaan testeillä, joissa verrataan malliratkaisun ja opiskelijan ratkaisun tulosteita. Testauksessa voidaan tutkia automaattisesti myös ohjelman rakennetta. Automaattisesta testauksesta kerrotaan asianomaisissa tehtävänannoissa ja kunkin tehtävän testit kuvaillaan tarkemmin WETOssa. WETO tulostaa rakennetestin epäonnistuessa lyhyen virheilmoituksen. Tarkista virheen tapahtuessa, että olet esitellyt testatun piirteen tehtävänannossa pyydetyllä tavalla. Esimerkiksi metodien näkyvyysmääreiden tulee olla tehtävänannossa määriteltyjä. Ota yhteyttä kurssin vastuuopettajaan, jos en keksi järjellisessä ajassa miksi WETO ei hyväksy ohjelmaasi. Huomaa, että ohjelmasi ei ole varmasti oikeellinen, vaikka testaus onnistuu, koska testit kattavat vain osan ohjelman toiminnallisuudesta ja rakenteesta.

Kurssin opettajat tarkistavat kullakin viikkoharjoituskerralla neljä tehtävää niiden rakenteen osalta. Ratkaisua arvioitaessa voidaan esimerkiksi tutkia onko luokan tiedot kätketty pyydetyllä tavalla ja onko luokassa kaikki tarvittavat metodit. Opettajat voivat tarkistaa ratkaisut myös tyylin eli hyvän ohjelmointitavan noudattamisen osalta. Tyylitarkistus voi kohdistua mihin tahansa tehtävään. Näin esimerkiksi huono sisennys voi tuottaa nollan, vaikka ohjelma läpäisee WETO-testit. Muista noudattaa hyvän ohjelmointitavan perusteita (Lausekielinen ohjelmointi I -kurssin luentomateriaalin 14. luku) ja uusia hyviä tapoja (Lausekielinen ohjelmointi II -kurssin luentomateriaalin 7. luku). Noudata myös tämän kurssin kuluessa opetettavia hyviä tapoja: Älä muun muassa nimeä aksessoreita kahta kieltä käyttäen. Tehtävänannossa kerrotaan, jos tehtävän ratkaisut arvioidaan tyylin osalta.

Tarkistus kohdistuu satunnaisesti valittuun otokseen palautuksia, jolloin osa ratkaisuista jää tarkistamatta ja on mahdollista, että tuurillakin voi saada pisteen. Tutustu mallivastauksiin vähintään tarkistettujen tehtävien osalta, jotta mahdolliset virheet omassa ratkaisussa eivät jäisi huomaamatta. Huonolaatuinen työ näkyy kuitenkin ennemmin tai myöhemmin nollina, koska otos on suuri. Varmin tapa hankkia ykkönen on tehdä kaikki tehtävässä pyydetty ja testata ohjelmaa.

Opettajat kirjoittavat WETOon kommentteja vain, kun nollan syy ei ole ilmiselvä. Kaikki muu palaute ratkaisuista annetaan keskitetysti alempana. Huomaa, että ratkaisuja ei voi korjata. Nolla vaihtuu ykköseksi vain, jos tarkistaja on tehnyt virheen.

Opiskelijoiden tulisi tarkistaa pisteensä arvosteluvirheiden varalta aina, kun kurssin postilistalla ja Ajankohtaista-palstalla viestitään harjoituksen pisteiden olevan valmiit. Näet harjoituksen x pisteet WETOssa Olio-ohjelmoinnin perusteet 2018 | Viikkoharjoitukset | Harjoitus x -kohdan Grading-välilehdeltä. Ota yhteyttä tehtävän tarkistajaan, mikäli epäilet tarkistuksessa tapahtuneen virheen. Ota yhteyttä ryhmän vetäneeseen opettajaan, jos läsnäolopiste puuttuu. (Luupin koodauspajan osalta yhteydenotot Jormalle.) Löydät mahdolliset kommentit nollaan johtaneista syistä tehtävän Grading-välilehdeltä, jossa on vielä napsautettava pisteen riviä, jotta kommentti avautuu.

Harjoitus 1

Tarkistetut tehtävät ja tehtävien tarkistajat olivat: 2) oppilaitos, Kari, 4) alkuaine, Kimmo, 5) kaivuri, Antti ja 6) oma luokka, Jorma.

2) Tiedonkätkennässä ei ollut juuri huomauttamista. Asteen asetusmetodissa olisi hyvä käyttää vakioita eikä numeroarvoja, koska vakiot on tehty ohjelmassa käytettäviksi.

4) Tiedonkätkentä oli hyvin hallussa tässäkin tehtävässä. Tyylin osalta olisi edelleen muistettava kommentit ja hyvät (riittävän pitkät) muuttujien nimet.

5) Kaivurin kaatumisongelmaa oli korjattu jonkin verran siten, että asettajassa ollut merkkijonon pituuden tarkistus oli poistettu. Tämä on virhe, koska myös korjatun asettajan tulisi tarkistaa ettei attribuutin arvoksi voi asettaa tyhjää merkkijonoa ””. Tehtävän mallivastauksessa on esitetty kuinka ongelman voi korjata ilman, että tarkistuksia on tarpeen karsia.

6) Monesta omasta luokasta puuttuivat lisämetodit. Luokassa tuli olla aksessoreiden lisäksi ainakin kaksi muuta metodia. Tästä ei rankaistu nyt, koska olio-ohjelmointi oli ensimmäisten harjoitusten aikaan hyvin uusi tuttavuus. Lisää puuttuvat metodit luokkaasi, jos ovat siitä unohtuneet, koska tarkistus tiukentuu kurssin edetessä. Nollat tulivat unohtuneista testiluokista. Unohduksia oli yllättävän paljon ja oli harmi antaa nolla varsinkin, kun moni oma luokka oli erittäin hyvin tehty.

Harjoitus 2

Tarkistetut tehtävät ja tehtävien tarkistajat olivat: 2) pipo, Kimmo, 4) alkuaine, Antti, 6) presidentti, Kari ja 7) oma luokka, Jorma.

2) Perimmäinen syy miksi static-määre tuli poistaa on osassa ratkaisuja jäänyt selittämättä. Tämän tapaisissa keksimistehtävissä pitäisi olla pyydetyt perustelut toimenpiteelle samaan tapaan kuin esimerkiksi matematiikan tentissä ei aivan riitä antaa oikeaa lopputulosta, vaan tarvitaan myös välivaiheet.Tehtävä arvioitiin opiskelijoiden onneksi hyvin liberaalisti ja osin vääräkin perustelu kelpasi. Tärkeintä oli, että oli kertonut edes jotain siitä mitä oli tehnyt ja miksi. Tutustu mallivastaukseen, jos ongelman syy jäi epäselväksi.

4) Hylkäyksiä oli tässä tehtävässä enemmän ja kaikkien nollien syy oli liittyi null-arvoon. Joko null-tarkistusta ei oltu tehty tai tarkistus oli vasta pituuden tarkistamisen jälkeen, jolloin ensimmäinen vertailu kaataa ohjelman, kun parametri on null-arvoinen.

6) Presidentti oli koodattu hyvin. Ongelmia ei juuri ollut. Muutama palauttaja oli lipsauttanut WETOon väärän version yliluokasta siten, että edelliseen tehtävään oli palautettu kyseisen tehtävän testit selvittävä valtionpäämies, mutta kuudennessa tehtävässä yliluokasta oli versio, joka ei edellisen tehtävän testejä selvittäisi. Versionhallinta tulee sitä tärkeämmäksi mitä pitemmälle kurssilla päästään.

7) Testiluokkia oli edelleen hukassa. Onneksi ei samassa määrin kuin ensimmäisissä harjoituksissa. Seuraavaksi yleisin ongelma oli jommankumman tai molempien rakentajien puuttuminen.

Harjoitus 3

Tarkistetut tehtävät ja tehtävien tarkistajat olivat: 3) ydinvoimala, Antti, 4) laite, Kari, 5) kettu, Kimmo ja 6) oma luokka, Jorma.

3) Ydinvoimalasta ei juuri muuta kuin, että Override-annotaatio kannattaa ottaa tavaksi. Muuten kaikki kunnossa.

4) Vakiot oli otettu hyvin haltuun. Tyylipuolella toiveissa olisi lisää kommentteja. Lukumetodeissa (gettereissä) ei tarvitse pohtia valintarakenteella mitä palauttaa; attribuutin arvon voi palauttaa suoraan return-lauseella.

5) Ketut oli tehty hyvin. Nollat tulivat attribuutin tunnuksen peittäneistä paino-nimisistä muuttujista.

6) Omassa luokassa pääasiallinen ongelma oli se, että metodin toteutus tai korvaus puuttui. Ali- ja yliluokat olivat valtaosin käsitteellisesti luontevia IsA-suhteella tarkistellen.

Harjoitus 4

Tarkistetut tehtävät ja tehtävien tarkistajat olivat: 2) vanhentuva leivos, Kari, 3) kissa, Kimmo, 5) laite, Antti ja 6) oma luokka, Jorma.

2) Rajapinta oli toteutettu sujuvasti. Nollat tulivat pääosin WETOn automaattitarkistajan toimesta.

3) Myös kissat olivat pääosin tyylikkäästi koodattuja. Muutama ratkaisu oli tehty pitemmän kaavan kautta. Tervehtiva-toteuteutuksen tarkistus instanceof-operaattoria käyttäen puuttui osasta ratkaisuja. Näissä tapauksissa opiskelijat saivat pitää pisteensä, koska testiluokka ei tutkinut ratkaisuja tältä osin.

5) Laitteen pääongelma olivat edelleen vakiot. Vakioita ei oltu joko käytetty tai sitten niitä oli käytetty siten, että ohjelma rikkoutuu, kun vakioiden arvot vaihtuvat. Kun ohjelmassa on annettu valmiita vakioita, niin pääsääntö on se, että vakiot on jateltu käytettäviksi. Vaikka kirjoitushetkellä literaali (esimerkiksi 3) saattaa vaikuttaa itsestään selvältä, se ei tule sitä olemaan enää muutaman kuukauden päästä kirjoittamisesta, eikä se ole sitä muille koodia lukeville. Vakiot ovat olemassa sitä varten, ettei tällaisia maagisia numeroita tarvitsisi koodiin ujuttaa.

Vakioiden suuruuden vertailu (esimerkiksi tila <= NUKKUU && tila >= SAMMUTETTU) ei ole tässä tehtävässä toivottavaa, koska vakiot ovat olemassa sitä varten, että niitä käyttäen voidaan viitata arvoon riippumatta siitä mikä se on. Jos joskus tulisi tarve esimerkiksi muuttaa SAMMUTETTU-vakion arvoksi 9, ei yllä esitetyn tapainen vertailu enää toimisi ajatellulla tavalla.

6) Tehtävä oli tehty hyvin ja koodin tyyli oli kohdallaan. Joukossa oli opettajan harmaata päivää piristäviä oivalluksia. Nollat tulivat implements-määrittelyn unohtumisesta. Java kääntää toteutukseksi ajatellun metodin iloisesti, vaikka sopimusta rajapinnan toteutuksesta ei ole tehty, koska ilman sopimusta kyseessä on kieliopillisesti luokan oma metodi.

Harjoitus 5

Tarkistetut tehtävät ja tehtävien tarkistajat olivat: 2) pipon korjaus, Kimmo, 4) toString, equals ja leivokset, Antti, 5) Comparable ja voimala, Kari ja 8) oma luokka, Jorma.

2) Pipot oli paikattu onnistuneesti. Tyylipuolen ongelmaksi nousi tällä kertaa sisennys.

4) Leivosten toString– ja equals-korvaukset olivat hyviä: merkkijonoesitykset ja vertailut tehtiin kunkin luokan sisällä luokan omista piirteitä käyttäen ja aliluokasta kutsuttiin yliluokan metodia super-attribuutin kautta, kuten piti. Equals-korvauksissa voi luottaa trycatch-lauseeseen, joka nappaa kiinni tyyppimuunnoksen epäonnistumisesta johtuvat poikkeukset, jolloin instanceof-operaattorille ei metodissa ole oikeastaan käyttöä.

5) Comparable-rajapinnan toteutuksessa on huomattava, että geneerisen ohjelmoinnin kautta päästään eroon suuresta määrästä tyyppimuunnoksia. Tehtävässä tyyppimuuttuja T kiinnitettiin Voimala-tyyppiin, jolloin compareTo-operaatiossa voimaloita voidaan vertailla näppärästi tehon suhteen ilman equals-metodissa tarvittavaa tyyppimuunnosta. Tyyppimuunnos tähän tapaan: Voimala toinenVoimala = (Voimala)parametri; on tarpeeton, koska parametriviite on jo valmiiksi Voimala-tyyppinen.

8) ToString-korvaukset oli tehty myös omissa luokissa juuri toivotulla tavalla super-attribuutin avulla ketjuttaen.

Harjoitus 6

Tarkistetut tehtävät ja tehtävien tarkistajat olivat: 2) laite ja akku, Antti, 3) syväkopiointi, Kari, 4) sanakirja, Kimmo ja 6) listan taulukointi, Jorma.

2) Kaikki tarkistetut ratkaisut hyväksyttiin. Ohjelmointiteknisinä seikkoina esiin nousivat turhat import-lauseet ja olioiden luominen ennen metodin parametreille tehtäviä tarkistuksia.

Turha import vaikuttaa ohjelman luettavuuteen, koska lukija tekee monesti oletuksia luokan sisällöstä jo import-lauseiden perusteella. Esimerkiksi java.io-pakkauksen käyttöön ottava luokka käsittelee todennäköisesti tiedostoja. Koodin lukija saattaa jopa jopa epäillä, että ohjelma on keskeneräinen tai rikki, jos luokissa on paljon import-lauseita, mutta ei pakkausten sisältöä käyttäviä lauseita.

Turhien olioiden luomista tulisi välttää, koska luomisoperaatio on yleensä sekä suoritusajalla ja muistinkäytöllä mitattuna ”kallis” operaatio. Esimerkiksi näin sanoen:

public Laite(int t) throws IllegalArgumentException {
   akku = new Akku(Akku.HYVA);
   if (t == SAMMUTETTU) {
      tila = t;
   }
   else {
      throw new IllegalArgumentException();
   }
}

Akku-olio luodaan myös silloin, kun parametrin arvo on jotain muuta kuin SAMMUTETTU. Harjoitustehtävien laajuisissa ohjelmissa ylimääräisillä olioilla ei ole ajan tai muistin kannalta mitään merkitystä, mutta laajemmissa ohjelmissa turhia olioita on syytä välttää. Yllä annettu rakentaja olisi tehokkaampi näin:

public Laite(int t) throws IllegalArgumentException {
   if (t == SAMMUTETTU) {
      tila = t;
      // Luodaan akku vain, jos laiteen luominen on mahdollista.
      akku = new Akku(Akku.HYVA); }
   else {
      throw new IllegalArgumentException();
   }
}

Ylimääräiset oliot vaikeuttavat – ylimääräisten rakentajien tapaan – koodin lukemista.

3) Kopiorakentajat oli tehty hyvin. Nollat tulivat käytännössä vain WETOn toimesta.

4) Sanakirjassa kaikki opettajan antamat nollat johtuivat puutteellisesta kommentoinnista. Metodeille tulisi muistaa kirjoittaa yleinen kommentti ennen otsikkoa ja tarkemmat kommentit metodin rungon sisään. Valmiissa rakenteissa on yleensä valmiita hakuoperaatioita, joita käyttämällä voi välttää itse kirjoitettuja silmukoita. HashMapissa get-operaatio silmukoi rakenteen tiedot läpi, jolloin ei ole tarvetta esimerkiksi hakea ensin pareja ja sitten silmukoida niitä läpi itse tehdyllä rakenteella.

6) Taulukoivan listaoperaatio oli neljännen tehtävän tapaan ratkaistu sujuvasti, mistä  vastuuopettaja on hyvin iloinen. Vastuuopettajan harmiksi kommentoinnissa oli neljännen tehtävän tapaan hieman oiottu. Nollat tulivat joko täysin tai lähes kommentoimattomista operaatioista.

Harjoitus 7

Tarkistetut tehtävät ja tehtävien tarkistajat olivat: 2) hakuoperaatio, Kimmo, 3) suurimman alkion hakeva operaatio, Antti, 4) yhtye ja levy, Kari ja 6) sähköisen tentin UML-kaavio, Jorma.

2) Enemmistö ratkaisuja oli suoraviivaisia. Muutamassa operaatiossa oli ylimääräistä monimutkaisuutta. Molemmilla tavoilla päästiin kuitenkin hyvin maaliin ja siten ei opettajan antamia nollia tästä tehtävästä.

3) Suurimman alkion hakevat operaatiot toimivat hyvin; kaikki WETOn hyväksymät ratkaisut hyväksyttiin. Haussa oli lyhempiä ja pitempiä ratkaisuja. Molemmissa on omat ongelmansa.

Tyhjään listaan liittyvästä tarkistuksesta pääsee eroon luottamalla alkio-operaatioon, joka palauttaa null-arvon myös, kun parametrina saatu indeksiarvo katsotaan virheelliseksi, koska lista on tyhjä. Lausumalla pelkästään:

Object suurin = alkio(0);

ja käyttämällä esiehtoista silmukkaa, tulosmuuttujaan saadaan oikea arvo (null) ilman erillistä tarkistusta tyhjän listan varalta. Ongelmana on se, että haku nojaa nyt hyvin vahvasti tiettyyn alkio-operaation ominaisuuteen. Näin lyhennetty operaatio menisi rikki, jos vastuuopettaja saisi esimerkiksi päähänsä ajatuksen muuttaa alkio-operaatiota siten, että virheen merkkinä heitetäänkin poikkeus.

Koodin lukijan on myös hankala ymmärtää kuinka erikoistapaukseen on varauduttu, jos kommenteissa säästellään juuri tässä kohdassa. Koodin selkeys saattaa toisaalta myös kärsiä, kun asioita lausutaan turhan monimutkaisesti.

Esimerkiksi tämä:

if (suurin == null) {
   return null;
}
else {
   return suurin; 
}

voidaan ilmaista yhdellä return-lauseella.

Yhteenvetona: yksinkertaisuuteen tulee pyrkiä, koska yksinkertainen koodi on luettavaa ja kaunista, mutta koodin eliminoinnissa on syytä ”kikkailla” vain pakottavista syistä.

WETO hylkäsi muutaman työn siksi, että listan suurimpaan alkioon liittyvä muuttuja (yllä suurin) oli alustettu kokonaisluvulla. Alkuarvon tyypin kiinnittävä operaatio toimii niin pitkään, kun listalla on viitteitä olioihin, joita voidaan vertailla muuttujan viittaamaan olioon. Pelkkä Comparable-rajapinnan toteutus kahdessa luokassa ei takaa vielä, että luokkien olioita voidaan vertailla. Esimerkiksi Integer– ja String-luokka toteuttavat Comparable-rajapinnan, mutta näiden luokkien olioiden vertailu ei ole mahdollista, koska luokkien compareTo-metodin on toteutettu siten, että niissä voidaan vertailla vain luokan omia olioita.

4) Opettajan hylkäämistä ratkaisuissa oli pääasiassa kaksi ongelmaa. Ensimmäinen ongelma oli se, että ratkaisuista puuttui joko kaikki aksessorit tai suuri osa niistä. Toinen ongelma oli puutteellinen parametrien tarkistus aksessoreissa ja rakentajissa.

6) Nollat tulivat kaaviosta puuttuvista luokista, väärän tyyppisistä suhteista ja assosiaatioiden nimien puuttumisesta. On syytä pitää mielessä, että luokkakaavion symboloiden välisten viivojen ulkoasulla on semattinen merkitys. Esimerkiksi tavallista assosiaatiota ei voi ilmaista nuolella, jonka päässä on ontto kolmiokärki, koska tällainen nuoli on varattu periytymisen ilmaisemiseen. Koostesuhdetta piirrettäessä viivan päässä oleva vinoneliö (”salmiakki”) tulisi muistaa sijoittaa sisältävän luokan puolelle.