Ohjelmoinnin perusteet - syksy 2007 Kurssikoe 17.10.2007 Tehtävän 1 arvosteluperusteet ja mallivastaus Tuomas Blom Tehtävä arvosteltiin "semi-kategorisesti" siten, että vastauksen yleisen laadun perusteella sille annettiin tietyt lähtöpisteet, joita sitten pienemmät virheet vielä pudottivat. Tämä lähtötaso riippui siitä, miten hyvin oli onnistunut tehtävän keskeisissä rakenteissa. Keskeiset rakenteet olivat: - konstruktori - lisääÖljyä-metodi - tankkaa-metodi - pääohjelma Näiden joukkoon voisi lukea myös luokan tietosisällön (sisäiset kentät/attribuutit), joka oli vähintään yhtä tärkeä, mutta siihen ei päde seuraavat arvostelusäännöt. Jos keskeisessä rakenteessa oli tehnyt useita toimintaan selvästi vaikuttavia virheitä, tai jos vastaus oli liian kaukana mistään Javaksi kutsuttavasta, rakenne tuomittiin epäonnistuneeksi. Lisäyksen ja tankkauksen metodeissa tätä ei vielä parista pienestä virheestä tapahtunut, mutta konstruktorissa ja pääohjelmassa oltiin hieman tarkempia. Tai pääohjelmassakin tärkeää oli lähinnä olion oikeaoppinen luonti ja käyttö, logiikkavirheitä tai "järkevyyttä" ei yritettykään metsästää. Lähtöpisteet määräytyivät siten seuraavasti: 16: Kaikki keskeiset osat pääasiassa kunnossa. 12: Yksi keskeinen osa epäonnistunut. 8: Kaksi keskeistä osaa epäonnistunut. 0: Jos kolme tai useampi keskeinen osa oli pielessä, arvostelutapa käännettiin ympäri ja virheiden etsimisen sijaan yritettiin löytää pisteiden arvoisia onnistumisia. Käytännössä maksimi oli tällöin 3. Jos lisääÖljyä- ja tankkaa-metodit olivat epäonnistuneet hyvin samanlaisella tavalla, ne saatettiin laskea vain noin puoleksitoista epäonnistumiseksi. Näitä oli tosin vain muutama. Lähtöpisteet eivät vielä taanneet mitään pisteminimiä, eli tarpeeksi monella pienemmällä virheellä 16 pisteestä saattoi pudota 12 alle jne. Seuraavassa yleisimpiä virheitä vakavuuksineen. Lista ei ole sikäli täysin absoluuttinen, että vastauksen kehnoontuessa virhetoleranssi hieman kasvoi, eli esim. negatiivisten parametrien tarkistamattomuudesta vähennettiin 8 lähtöpisteen vastauksissa vain yksi piste. - Tietosisältö Tietosisällössä siis kaivattiin neljää kenttää tyyliin: private double pääsäiliöVetoisuus, pääsäiliöMäärä, varasäiliöVetoisuus, varasäiliöMäärä; * Ei lainkaan kenttiä [-8] * Vajaa tietosisältö (vain vetoisuudet tai sisällöt tms.) [-4] - Konstruktori Lähes kaikki virheet olivat kahden pisten arvoisia. Yleisimmin parametreja ja kenttiä käsiteltiin väärin siten, ettei tarkistettuja arvoja koskaan sijoitettu kenttiin, tai tarkistuksen jälkeen kenttiin sijoitettiin kuitenkin alkuperäiset parametrit. * Jos parametreja ei tarkistanut, konstruktori katsottiin epäonnistuneeksi. - lisääÖljyä ja tankkaa * Negatiivisten parametrien tarkistus! Vain noin joka kymmenes oli muistanut tämän tehdä. [-2 / -1, jos puuttui vain toisesta] * Erilaisista logiikkavirheistä tyypillisin oli tilanteessa, jossa pääsäiliö piti täyttää (tai tyhjätä) kokonaan ja varastosäiliöön haluttiin lisätä se määrä, joka ei pääsäiliöön mahtunut (tai tankata se mitä ei pääsäiliöstä saatu). Tässä usein ensin täytettiin pääsäiliö ja vasta sitten laskettiin erotus "pääsäiliöVetoisuus-pääsäiliöMäärä", jonka tulos on nyt siis nolla. * Tankkauksessa ei tarvinnut täyttää pääsäiliötä varasäiliöstä tankkauksen jälkeen, mutta ei siitä rangaistukaan. - main() Pääohjelmassa riitti luoda uusi biodieselasema oikein ja kutsua edes paria sen metodia. Oikea luontitapahan on siis: BiodieselAsema asema = new BiodieselAsema(500, 1500); // (vetoisuudet esimerkkejä) Ja oikea tapa käyttää luotua asemaa vaikkapa: boolean onnistui = asema.lisääÖljyä(2200); // Pelkkä kutsukin riitti System.out.println("Asemalla öljyä " + asema.öljyäOn()); jne. * Pienetkin virheet konstruktorissa [-2] * Ei yhtään toimivaa metodikutsua [-2] * Yksittäiset virheet metodien kutsuissa [-1] * Aksessorien pääsäiliössäOn(), varastossaOn() ja öljyäOn() kutsumista pelkiltään ilman paluuarvon käyttöä ei yleensä rangaistu, paitsi jos niitä oli erityisen paljon ja vastaaja selvästi oletti niiden esim. tulostavan jotain itsestään. Tällöin [-1] - Syntaksi * Vastaus kirjoitettu tikkukirjaimin, eikä isoja kirjaimia merkitty erityisen selvästi [-1] * Sisennyksistä ja kehnoista muuttujanimistä ei rangaistu, ellen sitten niiden seurauksen ymmärtänyt vastusta väärin. * Metodien uloimpien aaltosulkeiden puuttumisesta ei tällä kertaa rangaistu. * Pienistä kirjoitusvirheistä ei yleensä välitetty, ellei sama virhe toistunut useasti. - Muut * Erilaisia loogisia ja syntaktisia virheitä on liikaa tässä eriteltäviksi, mutta lähes kaikista seurauksena oli yksi virhepiste. Vain aniharvasta sen enempää. Joissain tapauksissa erilliset virheet saatettiin niputtaa yhden virheen alle, lähinnä 8 lähtöpisteen vastauksissa. Lopuksi vielä esimerkkivastaus: public class BiodieselAsema { private double pääVetoisuus, varastoVetoisuus; private double pääÖljyä, varastoÖljyä; public BiodieselAsema(double pääsäiliö, double varastosäiliö) { if (pääsäiliö < 0) { pääsäiliö = 5000; } if (varastosäiliö < 0) { varastosäiliö = 5000; } pääVetoisuus = pääsäiliö; varastoVetoisuus = varastosäiliö; pääÖljyä = 0; varastoÖljyä = 0; } public double pääsäiliössäOn() { return pääÖljyä; } public double varastossaOn() { return varastoÖljyä; } public double öljyäOn() { return pääÖljyä + varastoÖljyä; } public boolean lisääÖljyä(double määrä) { double pääTila = pääVetoisuus - pääÖljyä; double varaTila = varastoVetoisuus - varastoÖljyä; // Lisäys epäonnistuu, jos parametri huono tai tilaa ei ole: if (määrä < 0 || määrä > (pääTila+varaTila)) { return false; } // Koko määrä ei mahdu pääsäiliöön if (määrä > pääTila) { pääÖljyä = pääVetoisuus; varastoÖljyä += (määrä - pääTila); } // Kaikki mahtuu pääsäiliöön else { pääÖljyä += määrä; } return true; } public double tankkaa(double määrä) { // Kehno parametri: if (määrä < 0) { return 0; } // Öljyä ei ole tarpeeksi, mutta annetaan mitä voidaan if (määrä > öljyäOn()) { määrä = öljyäOn(); pääÖljyä = 0; varastoÖljyä = 0; } // Öljyä on tarpeeksi, mutta ei yksin päävarastossa else if (määrä > pääÖljyä) { // Nämä lauseet olivat useissa vastauksissa siis väärin päin: varastoÖljyä -= (määrä - pääÖljyä); pääÖljyä = 0; } // Haluttu määrä saadaan pääsäiliöstä else { pääÖljyä -= määrä; } return määrä; } public String toString() { return "Pääsäiliössä " + pääÖljyä + "/" + pääVetoisuus + " | Varastosäiliössä " + varastoÖljyä + "/" + varastoVetoisuus; } public static void main(String[] args) { BiodieselAsema asema = new BiodieselAsema(1000, 2000); System.out.println(asema); // Kutsuu aseman toString()-metodia System.out.print("Lisätään 2000 litraa ... "); if (asema.lisääÖljyä(2000)) { System.out.println("onnistui!"); } else { System.out.println("epäonnistui!"); } System.out.println(asema); System.out.print("Lisätään toiset 2000 litraa ... "); if (asema.lisääÖljyä(2000)) { System.out.println("onnistui!"); } else { System.out.println("epäonnistui!"); } // Kokeillaan nyt näitäkin metodeja System.out.println("Pääsäiliössä öljyä " + asema.pääsäiliössäOn()); System.out.println("Varasäiliössä öljyä " + asema.varastossaOn()); System.out.print("Valtamerilaiva haluaa tankata 1300 litraa ... "); double saatu = asema.tankkaa(1300); System.out.println("sai " + saatu + " litraa."); System.out.println(asema); System.out.print("Toinenkin valtamerilaiva haluaa tankata 1300 litraa ... "); saatu = asema.tankkaa(1300); System.out.println("sai " + saatu + " litraa."); System.out.println(asema); } }