Skip to content

laskari 6

Matti Luukkainen edited this page Apr 29, 2016 · 22 revisions
Tehtävien palautuksen deadline maanantaina 2.5. klo 23.59

Ohjausta tehtävien tekoon to 28.4. 14-17 B221

palautetaan GitHubin kautta

  • palautusta varten voit käyttää samaa repoa kuin jonkin aiemman viikon tehtävissä
  • palautusrepositorion nimi ilmoitetaan tehtävien lopussa olevalla palautuslomakkeella

Java 8

Useimmissa viikon tehtävistä voi ja kannattaakin hyödyntää Java 8:ia. Jos haluat tehdä näin, muuta pom.xml:stä source maven-compiler-plugin:in source- ja target-versioiksi 1.8.

1. Laskin ja komento-oliot

Repositorion https://github.com/mluukkai/ohtu2016 hakemistosta viikko6/Laskin löytyy hieman modifioitu versio Ohjelmoinnin jatkokurssin viikon 5 tehtävästä.

Sovellusta on laajennettu lisäämällä siihen painike undo-toiminnallisuutta varten, undoa ei kuitenkaan ole vielä toteutettu.

Sovelluksen varsinainen toimintalogiikka on luokassa Tapahtumankuuntelija. Koodissa on tällä hetkellä hieman ikävä if-hässäkkä:

    @Override
    public void actionPerformed(ActionEvent ae) {
        int arvo = 0;
 
        try {
            arvo = Integer.parseInt(syotekentta.getText());
        } catch (Exception e) {
        }
 
        if (ae.getSource() == plus) {
            sovellus.plus(arvo);
        } else if (ae.getSource() == miinus) {
            sovellus.miinus(arvo);
        } else if (ae.getSource() == nollaa) {
            sovellus.nollaa();
        } else {
            System.out.println("undo pressed");
        }
        
        int laskunTulos = sovellus.tulos();
         
        syotekentta.setText("");
        tuloskentta.setText("" + laskunTulos);
        if ( laskunTulos==0) {
            nollaa.setEnabled(false);
        } else {
            nollaa.setEnabled(true);
        }
        undo.setEnabled(true);
    }

Refaktoroi koodi iffittömäksi luennolla 8 esiteltyä suunnittelumallia komento-olio käyttäen.

Tässä tehtävässä ei tarvitse vielä toteuttaa undo-komennon toiminnallisuutta!

Luokka Tapahtumankuuntelija voi näyttää refaktoroituna esim. seuraavalta:

public class Tapahtumankuuntelija implements ActionListener {
    private JButton nollaa;
    private JButton undo;
    private Sovelluslogiikka sovellus;
    private Map<JButton, Komento> komennot;
    private Komento edellinen;
 
    public Tapahtumankuuntelija(JButton plus, JButton miinus, JButton nollaa, JButton undo, JTextField tuloskentta, JTextField syotekentta) {
        this.nollaa = nollaa;
        this.undo = undo;
        this.sovellus = new Sovelluslogiikka();
        komennot = new HashMap<>();
        komennot.put(plus, new Summa(sovellus, tuloskentta, syotekentta));
        komennot.put(miinus, new Erotus(sovellus, tuloskentta, syotekentta));
        komennot.put(nollaa, new Nollaa(sovellus, tuloskentta, syotekentta));
    }
    
    @Override
    public void actionPerformed(ActionEvent ae) {
 
        Komento komento = komennot.get(ae.getSource());
        if  (komento!=null) {
            komento.suorita();
            edellinen = komento;
        } else {
            // toiminto oli undo
            edellinen.peru();
            edellinen = null;
        }
        
        nollaa.setEnabled(sovellus.tulos()!=0);
        undo.setEnabled(edellinen!=null);
    }
 
}

Komennoilla on nyt siis kaksi julkista metodia void suorita() ja void peru().

2. Undo

Toteuta nyt laskimeen myös undo-toiminnallisuus. Periaatteena on siis tallettaa jokaiseen komentoon sen verran dataa, että kutsuttaessa metodia peru komento osaa palauttaa tilanteen joka oli voimassa (eli käytännössä laskimen arvon) ennen komennon suoritusta.

Riittää että ohjelma muistaa edelliseksi suoritetun komennon, eli undo-toimintoa ei tarvitse osata suorittaa kahta tai useampaa kertaa peräkkäin. Tosin komento-olio-suunnittelumallin avulla olisi melko helppo toteuttaa myös useamman undo- tai redo-toiminnallisuuden hallitseva sovellus.

3. Kyselykieli NHLStatistics-ohjelmaan

Repositorion https://github.com/mluukkai/ohtu2016 hakemistosta viikko6/QueryLanguage löytyy jälleen yksi versio tutusta NHL-tilastoja lukevasta ohjelmasta.

Tällä kertaa olemme kiinnostuneita tekemään hieman monimutkaisempia "kyselyjä" pelaajatietoihin, esim. listaa kaikki joukkueen PHI pelaajat joilla on vähintään 5 maalia ja vähintään 10 syöttöä.

Koodin onkin luotu hieman valmista kalustoa josta pääset liikkeelle. Edelläolevan kyselyn voi suorittaa seuraavasti:

public static void main(String[] args) {
    Statistics stats = new Statistics(new PlayerReaderImpl("http://nhlstatistics.herokuapp.com/players.txt"));
 
    Matcher m = new And( new HasAtLeast(5, "goals"),
                         new HasAtLeast(10, "assists"),
                         new PlaysIn("PHI")
    );
 
    for (Player player : stats.matches(m)) {
        System.out.println( player );
    }
}

Luokalle Statistics on tehty metodi matches, joka palauttaa listan pelaajista joille parametrina annettu Matcher-rajapinnan toteuttava olio palauttaa true

Tutustu ohjelman rakenteeseen

  • huomioi miten HasAtLeast käyttää Javan ns. reflektio-ominaisuutta kutsuessaan merkkijonoparametria vastaavaa metodia
  • toinen huomioinarvoinen piirre on And-luokan konstruktorissa käytetty vaihtuvamittainen parametrilista, eli "vararg", ks. lisää esim: http://www.javadb.com/using-varargs-in-java

Tee rajapinnan Matcher toteuttavat luokat, joiden avulla voit tehdä operaatiot

  • HasFewerThan (HasAtLeast-komennon negaatio eli, esim. on vähemmän kuin 25 maalia)
  • or
  • not

Tee erilaisia kyselyjä, ja varmista että uudetkin operaatiot toimivat

Kyselyt perustuvat rakenteeltaan decorator-suunnittelumalliin, vastaavasti kuten luennon 9 dekoroitu pino. And- ja OR-muotoiset kyseltyt on muodostetty composite-suunnittelumallin hengessä, ne ovat Matcher-rajapinnan toteuttavia olioita, jotka sisältävät itse monta Matcher-olioa. Niiden käyttäjä ei kuitenkaan tiedä sisäisestä rakenteesta mitään.

4. Parannettu kyselykieli

Matcher-olioiden avulla tehtyä kyselykieltä vaivaa se, että kyselyjen rakentaminen on hieman ikävää, sillä jokaista kyselyn osaa kohti on luotava new-komennolla uusi olio. Tee luennon 9 pinorakentajan hengessä kyselyrakentaja, jonka avulla voit luoda Matcher-olioita.

Rakentaja voi toimia esim. seuraavaan tapaan.

Ensin kysely missä tulostetaan pelaajat joiden joukkue on NYR, joilla on vähintään 10 mutta vähemmän kuin 25 maalia:

public static void main(String[] args) {
    Statistics stats = new Statistics(new PlayerReaderImpl("http://nhlstatistics.herokuapp.com/players.txt"));
 
    QueryBuilder query = new QueryBuilder();
 
    Matcher m = query.playsIn("NYR")
                     .hasAtLeast(10, "goals")
                     .hasFewerThan(25, "assists").build();
 
    for (Player player : stats.matches(m)) {
        System.out.println( player );
    }

Peräkkäin ketjutetut ehdot siis toimivat "and"-periaatteella.

Or-ehdon sisältävä komento voi olla muodostettu esim. seuraavasti:

Matcher m1 = query.playsIn("PHI")
                  .hasAtLeast(10, "goals")
                  .hasFewerThan(20, "assists").build();
 
Matcher m2 = query.playsIn("EDM")
                  .hasAtLeast(50, "points").build();
 
Matcher m = query.oneOf(m1, m2).build();

Tai kaikki sama ilman apumuuttujia:

Matcher m = query.oneOf(
                        query.playsIn("PHI")
                             .hasAtLeast(10, "goals")
                             .hasFewerThan(15, "assists").build(),
 
                        query.playsIn("EDM")
                             .hasAtLeast(50, "points").build()
                       ).build();

Rakentajasi ei ole pakko toimia samalla tavalla.

5. Git: rebase

Lue http://git-scm.com/book/en/Git-Branching-Rebasing

Aikaansaa seuraavankaltainen tilanne

------- master
\
 \--- haara

"rebeissaa" haara masteriin, eli aikaansaa seuraava tilanne:

------- master
       \
        \--- haara

Varmista komennolla gitk --all että tilanne on haluttu.

"mergeä" master vielä haaraan:

-------
       \     master
        \--- haara

Lopputuloksena pitäisi siis olla lineaarinen historia ja master sekä haara samassa. Varmista jälleen komennolla gitk --all että kaikki on kunnossa.

Poista branch haara. Etsi googlaamalla komento jolla saat tuhottua branchin.

6. GitHub: pull request ja refaktorointia (tätä tehtävää ei lasketa versionhallintatehtäväksi)

Isoa projektia on vaikea ylläpitää yksin ja vielä vaikeampaa on löytää oikeat ratkaisut jokaiseen ongelmaan, kun osa-alueitakin rupeaa jo kertymään useita. On vaikeaa olla joka paikan höylä ja jotkin osa-alueet eivät välttämättä edes miellytä ja niihin on siksi vaikea paneutua. Saatat löytää itsesi ajattelemasta vaikkapa: "Lukisipa joku tietorakenteiden asiantuntija tämän osuuden läpi ja tsekkaisi, että HashSetti on nyt varmasti se tehokkain ratkaisu...". Ehkäpä et edes ajatellut asiaa, mutta joku silti osoittaa, että puurakenne olisi tässä tehokkaampi ratkaisu. Mokoma tekee vielä korjauksetkin puolestasi lähdekoodiin ja pistää pullrequestin. Onneksi julkaisit projektisi Open Sourcena!

Kontribuutiotasi kaivataan! GitHub on täynnä Open Source -projekteja, jotka kaipaavat panostasi. Mikäs sen kivempaa, kuin käyttää muutama tunti suosikki repositioriosi lähdekoodin parissa ja korvata sieltä huomaamasi tehoton algoritmi mielestäsi paremmalla ratkaisulla. Useilla repositorioilla on valmiit ohjeet contribuuttamiseen Contributing.md:ssä repositorion juuressa. Tässä esimerkiksi bluebird.js:än CONTRIBUTING.md.

Tehtävänäsi on harjoitella contribuuttamista ja vieraan koodin refaktorointia.

  • Valitse yksi ryhmä miniprojektien joukosta
    • huom: jos teet tehtävän ennen viikonloppua, valitse sellainen ryhmä jonka viikon asiakastapaaminen on jo mennyt.
  • Forkkaa sellaisen ryhmän repositorio, jolla ei ole jo viittä pull requestia. Jos ryhmällä on jo viisi pullrequestia, valitse jokin toinen ryhmä
  • Tee uusi branch nimellä "muutoksia"
  • Tee luomaasi branchiin "tyhjä" pull request: Eli lisää vaikka yksi tyhjä rivi README.md:hen, pushaa uusi branch GitHubiin ja tee branchista pull request. Tyhjän pullrequestin tarkoituksena on varata sinulle paikka kyseisen repositorion contribuuttajien joukosta. Haluamme, että kaikki ryhmät saavat tasaisesti pullrequesteja ja siksi olemme rajanneet maksimimäärän viiteen. Jos kaikki ryhmät ovat saaneet jo viisi pullrequestia niin valitse joukosta mieleisesi
  • Etsi ryhmän lähdekoodista jotain refaktoroitavaa
  • Refaktoroi ja committaa
  • Käy katsomassa tekemääsi tyhjää pullrequestia. Mitä tapahtui?
  • Rebase luomasi branch paikalliseen master branchin päälle. Pushaa. Tapahtuiko pull requestissa muutoksia?
  • Otsikoi tekemäsi pullrequest niin, että se kuvaa tekemiäsi muutoksia. Tarkenna otsikon alle mitä teit ja miksi.
  • Jos ryhmä pyytää sinua tekemään muutoksia pullrequestiisi, tee tarvittavat muutokset ja committaa. Päivittyikö pullrequest?
  • Kun ryhmä on hyväksynyt muutoksesi, voit poistaa luomasi branchin

Laita palautusrepositorioosi tiedosto PULL.md ja sen sisällöksi linkki pullrequestiin.

7. git: stash

Lue http://git-scm.com/book/en/Git-Tools-Stashing kohtaan Un-applying a Stash asti.

Oletetaan että olet repositoriossa, jossa on ainakin kaksi branchia: master ja joku toinen (kutsutaan sitä tässä nimellä toinen).

  • ollessasi master-branchissa tee muutoksia, joita lisäät staging-alueelle ja joitain muutoksia joita et vielä "äddää"
  • pomosi käskee sinua välittömästi tekemään pari muutosta branchiin toinen. Et kuitenkaan halua vielä comittoida masterissa olevia muutoksia
  • jos siirryt branchiin toinen tekemättä comittia, tulee hirveä sotku, sillä muutokset pysyvät muutoksina toisessakin branchissa
  • stashays pelastaa tästä tilanteesta, eli stashaa masterissa olevat muutoset
    • kokeile ennen ja jälkeen stash-komennon komentoa git status
  • siirry branchiin toinen, tee sinne joku muutos jonka committaat
  • palaa jälleen masteriin
  • palauta stashatyt muutokset komennolla git stash apply
    • varmista että muutokset palasivat
    • kuten huomaat, staging-alueelle jo lisätty muutos ei palaa staging-alueelle, vaan joudut lisäämään sen uudelleen
    • jos edellisessä komento olisi annettu muodossa git stash apply --index, olisi tilanne palautunut täysin ennalleen

tehtävien kirjaaminen palautetuksi

tehtävien kirjaus:

  • Kirjaa tekemäsi tehtävät tänne
    • huom: tehtävien palautuksen deadline on maanantaina 2.5. klo 23.59

palaute tehtävistä:

  • Lisää viikon 1 tehtävässä 11 forkaamasi repositorion omalla nimelläsi olevaan hakemistoon tiedosto nimeltä viikko6
  • tee viime viikon tehtävän tapaan pull-request
    • anna tehtävistä palautetta avautuvaan lomakkeeseen
    • huom: jos teeh tehtävät alkuviikosta, voi olla, että edellistä pull-requestiasi ei ole vielä ehditty hyväksyä ja et pääse vielä tekemään uutta requestia