Ohjelmistojen mallintaminen, kesä 2011, laskuharjoitus 2

Kaikissa tehtävissä ei ole yhtä oikeaa vastausta -- tärkeää on pohdinta ja yrittäminen. Jos et ole käyttänyt Javaa, voit ohjelmoida tehtäviin vastauksia myös muilla kielillä. Jos ohjelmointitaitosi ovat ruosteessa, harjoittelu on tie onneen. Jos et saa ohjelmiasi toimimaan, voit myös kuvailla sanallisesti niiden toiminnan.

Tarkoitus on, että teet tehtävät etukäteen ennen laskuharjoitustilaisuutta.

  1. LAL on vasta perustettu suomalainen yritys, jonka toimiala on valtakunnallinen linja-autoliikenne. Tehtävänä on määritellä LAL:n tarvitsema tietojärjestelmä. Yrittäjän edustaja on kertonut sähköpostitse yrityksen liike-ideasta seuraavaa:

    LAL - suomalaista linja-autoliikennettä

    "Aiomme kilpailla asiakkaista alhaisilla hinnoilla. Saavutamme alhaiset hinnat soveltamalla kaukoliikenteessä halpalentoyhtiöiden inspiroimaa, kustannukset minimoivaa liiketoimintamallia: liikennöimme vain suosittuja, kahden suuren taajaman välisiä suoria reittejä emmekä takaa jatkoyhteyksiä.

    Pyrimme saamaan linja-autot juuri ja juuri täyteen mukauttamalla lippujen hinnat kysyntään. Palkitsemme lippunsa aikaisin ostavat matkustajat halvemmilla hinnoilla. Huonosti täyttyvän lähdön hintaa voimme laskea, suositun liikenneyhteyden hintaa taas nostaa. Samalla bussilla matkustavat ovat voineet siis maksaa matkastaan erilaisia hintoja ostohetkestä riippuen. Matkustajaryhmästä riippuvia alennuksia tai paljousalennuksia emme anna.

    Myymme lippuja vain verkkopalvelun kautta, emme linja-autoasemilla emmekä autoissa. Liput on maksettava luottokortilla tai verkkopankkia käyttäen. Lippua ei voi varata. Lippu myydään tiettyyn vuoroon, muttei tietylle istumapaikalle. Lipun voi perua, mutta palautamme lipun hinnasta vain osan. Perumisen voi hoitaa internetin lisäksi puhelinpalvelumme avulla. Mitä lähempänä lähtö on, sitä pienempi osa hinnasta palautetaan. Lippujen perumisen lisäksi puhelinpalvelusta on mahdollista tiedustella linja-autojen aikataulutietoja.

    Liput ovat sähköpostitse toimitettavia elektronisia lippuja. Kuljettaja tarkistaa matkustusoikeuden pysäkillä lukemalla lipun viivakoodin bussissa olevalla päätteellä.

    Palkkaamme palvelukseemme työntekijöitä erilaisiin tehtäviin. Liikennesuunnittelija perustaa ja lakkauttaa linjoja ja vuoroja kysynnän mukaan sekä suunnittelee aikataulut. Hinta-analyytikko mukauttaa hintoja kysynnästä ja kilpailijoista riippuen. Ajojärjestelijä toimii ajajien esimiehenä ja osoittaa kullekin ajolle kuljettajan ja linja-auton ja ajoittaa ajoneuvojen huollot ja katsastukset.

    Kuljettajille ja puhelinpalvelusta huolehtiville asiakaspalvelijoille maksamme tuntipalkkaa. Muu henkilökunta saa kuukausipalkkaa. Ydintoimintaan kuulumattomat toiminnat (kirjanpito, palkanmaksu, autojen huolto, tietotekniikan ylläpito jne.) aiomme ulkoistaa."

    Tunnista tietojärjestelmän käyttäjät.


  2. Listaa kaikki keksimäsi LAL:in tietojärjestelmän käyttötapaukset. Käyttötapauksesta riittää mainita nimi, käyttäjät ja käyttötapauksen tavoite lyhyesti.

  3. Piirrä käyttötapauskaavio edelliseen tehtävään liittyen.

  4. Alla on viime laskuharjoituksista tuttu Matkakorttiohjelma. Piirrä ohjelmasta UML-luokkakaavio, jossa jokaiselle luokalle on määritelty metodit ja attribuutit. Ota huomioon sekä pysyvät että väliaikaiset yhteydet! (Väliaikaisia yhteyksiä, joilla kuvataan mm. luontia ja käyttöä, käsitellään tiistaina.)
    public class Kioski {
      public Matkakortti ostaMatkakortti(String nimi) {
        Matkakortti uusiKortti = new Matkakortti(nimi);
        return uusiKortti;
      }
    
      public Matkakortti ostaMatkakortti(String nimi, int arvo) {
        Matkakortti uusiKortti = new Matkakortti(nimi);
        uusiKortti.kasvataArvoa(arvo);
        return uusiKortti;
      }
    }
    
    public class Matkakortti {
      private String omistaja;
      private double arvo;
      private int pvm;
      private int kk;
      
      public Matkakortti(String n) {
        omistaja = n; 
        pvm = 0; 
        kk = 0; 
        arvo = 0;
      }
    
      public void kasvataArvoa(double a) { 
        arvo += a; 
      }
    
      public void vahennaArvoa(double a) { 
        arvo -= a; 
      }
    
      public double getArvo(){ 
        return arvo; 
      }
      
      public void uusiAika(int p, int k) {
        kk = k;
        pvm = p;
      }
    }
    
    public class Lataajalaite {
      public void lataaArvoa(Matkakortti k, double a) {
        k.kasvataArvoa(a);
      }
    
      public void lataaAikaa(Matkakortti k, int pvm, int kk) {
        k.uusiAika(pvm, kk);
      }
    }
    
    public class Lukijalaite {
      private double RATIKKA = 1.5;
      private double HKL = 2.1;
      private double SEUTU = 3.5;
    
      public boolean ostaLippu(Matkakortti k, int tyyppi){
        double hinta = 0;
        
        if ( tyyppi == 0 ) hinta = RATIKKA;
        else if ( tyyppi ==1 ) hinta = HKL;
        else hinta = SEUTU;
       
        if ( k.getArvo() < hinta ) return false;
        
        k.vahennaArvoa(hinta);
        return true;
      }
    
      public boolean onkoVoimassa(Matkakortti k) {
        // onkoVoimassa-metodi lienee hieman buginen
        if (k.getArvo() > 100) {
          return false;
        } 
    
        return true;
      } 
    }
    
    public class Main {
    
      public static void main(String[] args) {
        Lataajalaite rautatietori = new Lataajalaite();
        Lukijalaite ratikka6 = new Lukijalaite();
        Lukijalaite bussi244 = new Lukijalaite();
    
        Kioski lippuLuukku = new Kioski();
        Matkakortti artonKortti = lippuLuukku.ostaMatkakortti("Arto");
        rautatietori.lataaArvoa(artonKortti, 3);
        ratikka6.ostaLippu(artonKortti, 0);
        bussi244.ostaLippu(artonKortti, 2);
      }
    }
    

  5. Alla on kuvattu yksinkertainen Onnenpyoräohjelma. Onnenpyorälla on viisari jolla on arvo, ja onnenpyörää voi pyöräyttää. Ohjelmaan liittyvän Main-luokan rivit on numeroitu. Piirrä kolme oliokaaviota: ensimmäinen kuvaa ohjelman tilaa rivin 6 suorituksen jälkeen, toinen ohjelman tilaa rivin 8 suorituksen jälkeen ja kolmas ohjelman tilaa rivin 10 suorituksen jälkeen. Mikä on olion "punainen" kohtalo rivin 8 jälkeen?
    public class Viisari {
      private int arvo;
      private int maksimi;
      
      public Viisari(int arvo, int maksimi) {
        this.arvo = arvo;
        this.maksimi = maksimi;
      }
    
      public void kasvata() {
        this.arvo = this.arvo + 1;
        if (this.arvo > this.maksimi) {
          this.arvo = 0;
        } 
      }
    
      public int annaArvo() {
        return this.arvo;
      } 
    }
    
    
    public class Onnenpyora {
      private Viisari viisari;
    
      public Onnenpyora(Viisari viisari) {
        this.viisari = viisari;
      }
    
      public void asetaViisari(Viisari viisari) {
        this.viisari = viisari;
      } 
    
      public void pyorita() {
        int luku = new Random().nextInt(10000);
        for(int i = 0; i < luku; i++) {
           this.viisari.kasvata();
        }  
      }
    
      public int annaArvo() {
        return this.viisari.getArvo();
      } 
    }
    
    
    1.  public class Main {
    2. 
    3.    public static void main(String[] args) {
    4.      Viisari punainen = new Viisari(100);
    5.      Onnenpyora onni = new Onnenpyora(punainen);
    6.      Viisari sininen = new Viisari(500);
    7.      onni.pyorayta();
    8.      onni.asetaViisari(sininen);
    9.      onni.pyorayta();
    10.     Onnenpyora epaonni = new Onnenpyora(sininen);
    11.   }
    12. }
    
    

  6. Harrastat pariohjelmointia kaverisi kanssa. Kaverisi on määritellyt seuraavat testit ja sinun tulee toteuttaa toiminnallisuus, jonka avulla testit menevät läpi. Kerro mitä ohjelman on tarkoitus tehdä. Kerro myös mahdollisista ongelmista kaverisi testaustyylissä.

  7. import org.junit.Assert;
    import org.junit.Test;
    
    public class LaskinTest {
    
      @Test
      public void testLaskimenLuonti() {
        Laskin laskin = new Laskin();
      }
    
      @Test
      public void testaaSummaus() {
        Laskin laskin = new Laskin();
    
        int ekaArvo = 300;
        int tokaArvo = 600;
    
        Assert.assertEquals(900, laskin.summa(ekaArvo, tokaArvo), 0.0);
      }
    
      @Test
      public void testaaErotus() {
        Laskin laskin = new Laskin();
    
        int ekaArvo = 300;
        int tokaArvo = 600;
    
        Assert.assertEquals(-300, laskin.erotus(ekaArvo, tokaArvo), 0.0);
      }
    
      @Test
      public void testaaKerto() {
        Laskin laskin = new Laskin();
    
        int ekaArvo = 300;
        int tokaArvo = 600;
    
        Assert.assertEquals(300*600, laskin.kerto(ekaArvo, tokaArvo), 0.0);
      }
    
      @Test
      public void testaaJako() {
        Laskin laskin = new Laskin();
    
        int ekaArvo = 300;
        int tokaArvo = 600;
    
        Assert.assertEquals(0.5, laskin.jako(ekaArvo, tokaArvo), 0.0);
      }
    }
    
  8. Olet töissä projektissa, jossa testit kirjoitetaan vasta ohjelmalogiikan toteutuksen jälkeen. Olet testausvuorossa. Kaverisi on toteuttanut seuraavan Valuuttamuunnin-luokan, joka kaverisi mukaan hoitaa valuuttamuunnoksia. Kirjoita luokalle vähintään kuusi testiä, jotka testaavat luokan toiminnallisuutta. Testien tulee testata jokaista luokan metodia.
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import java.util.TreeMap;
    
    /**
     * Kurssit perustuvat 5.8.2011 valuuttakursseihin.  Valuuttojen käännös
     * tapahtuu aina euron kautta.
     * 
     */
    public class Valuuttamuunnin {
      private Map<String, Double> valuuttakurssit;
      private Map<String, String> valuuttaselitteet;
    
      public Valuuttamuunnin() {
        valuuttakurssit = new HashMap();
        valuuttaselitteet = new TreeMap();
    
        valuuttakurssit.put("EUR", 1.0);
        valuuttaselitteet.put("EUR", "Euroopan euro");
    
        valuuttakurssit.put("USD", 1.4155);
        valuuttaselitteet.put("USD", "Yhdysvaltain dollari");
      
        valuuttakurssit.put("RUB", 40.0645);
        valuuttaselitteet.put("RUB", "Venäjän rupla");
    
        valuuttakurssit.put("GBP", 0.86905);
        valuuttaselitteet.put("GBP", "Iso-Britannian punta");
    
        valuuttakurssit.put("JPY", 111.25);
        valuuttaselitteet.put("JPY", "Japanin jeni");
      }
    
      public void lisaaKurssi(String lyhenne, String valuutta, double kerroin) {
        valuuttaselitteet.put(lyhenne, valuutta);
        valuuttakurssit.put(lyhenne, 111.25);
      }
    
      public void vaihdaKurssi(String lyhenne, double arvo) {
        valuuttakurssit.put(lyhenne, arvo);
      }
    
      public double muunnaValuutta(String lahtovaluutta, String kohdevaluutta, double maara) {
        Set<String> valuutat = valuuttakurssit.keySet();
        if(!valuutat.contains(lahtovaluutta)) {
          throw new IllegalArgumentException("Valuuttaa " + lahtovaluutta + " ei ole listattu muuntimeen.");
        }
    
        if(!valuutat.contains(kohdevaluutta)) {
          throw new IllegalArgumentException("Valuuttaa " + kohdevaluutta + " ei ole listattu muuntimeen.");
        }
    
        double lahtokurssi = valuuttakurssit.get(lahtovaluutta);
        double kohdekurssi = valuuttakurssit.get(kohdevaluutta);
    
        maara = maara / lahtokurssi;
        return maara * kohdekurssi;
      }
    }
    

  9. Korjaa kaverisi edellisessä tehtävässä tekemä(t) virhe(et).

  10. Alla on kuvattu ohjelma, jossa käytetään tehtäviin 6 ja 7 liittyviä luokkia. Kuvaa ohjelman kulkua sekvenssikaavion avulla (sekvenssikaavioihin tutustutaan kevyesti tiistaina).

  11. public class Main {
    
      public static void main(String[] args) {
        Laskin laskin = new Laskin();
        Valuuttamuunnin muunnin = new Valuuttamuunnin();
    
        double tuhatUSDEURMuuntimella = muunnin.muunnaValuutta("USD", "EUR", 1000);
        double tuhatUSDEURLaskimella = laskin.jako(1000, 1.4155);
    
        if (tuhatUSDEURMuuntimella != tuhatUSDEURLaskimella) {
          System.out.println("USD-EUR ei toimi!");
        } else {
          System.out.println("USD-EUR toimii");
        }
    
        double tuhatUSDRUBMuuntimella = muunnin.muunnaValuutta("USD", "RUB", 1000);
        double tuhatUSDRUBLaskimella = laskin.jako(1000, 1.4155);
        tuhatUSDRUBLaskimella = laskin.kerto(tuhatUSDRUBLaskimella, 40.0645);
    
        if (tuhatUSDRUBMuuntimella != tuhatUSDRUBLaskimella) {
          System.out.println("USD-RUB ei toimi!");
        } else {
          System.out.println("USD-RUB toimii");
        }
      }
    }
    
  12. Kuvaa jokaista tämän viikon tehtävissä esiintynyttä tekniikkaa muutamalla lauseella. Kerro missä ohjelmistokehityksen vaiheissa niitä käytetään.