Skip to content

Latest commit

 

History

History
318 lines (234 loc) · 19.6 KB

2.md

File metadata and controls

318 lines (234 loc) · 19.6 KB
Tehtävien palautuksen deadline su 26.3. klo 23.59

ohjausta tehtävien tekoon to 12-14 ja pe 14-16 salissa B221

palautetaan GitHubin kautta

  • tehtävät 4 ja 5 tehdään paikalliseen repositorioon, eli ne eivät näy palautuksessa mitenkään
  • tee palautusta varten uusi repositorio tai käytä viikon 1 repositoriotasi
  • palautusrepositorion nimi ilmoitetaan tehtävien lopussa olevalla palautuslomakkeella

huomio gradleen liittyen

Käytämme tälläkin viikolla gradle-muotoisia projekteja. Jos gradle-koodi lukee syötteitä komentoriviltä, tulee määrittelytietdostojen loppuun liittää seuraava

run {
    standardInput = System.in
}

Ilman tätä määrittelyä ohjelmaa gradlella suorittaessa, eli komennolla gradle run, ohjelma ei pääse käsiksi syötevirtaan ja scannerin luominen epäonnistuu.

Tämän viikon tehtäviin liittyviin projekteihin määrittey on jo lisätty.

Jos käytät komentorivisyötettä, kannattaa ohjelma suorittaa komennolla gradle -q run, jolloin gradle jättää omat tulostuksensa tekemättä.

1. riippuvuuksien injektointi osa 1

2. riippuvuuksien injektointi osa 2: NHL-tilastot

  • kurssirepositorion hakemistossa koodi/viikko2/NHLStatistics1 on ohjelma, jonka avulla on mahdollista tutkia http://nhl.com-sivulla olevia, vuoden 2013-14 tilastotietoja

  • Ohjelma koostuu kolmesta luokasta.

    • Statistics on palvelun tarjoava luokka, se tarjoaa metodit yhden pelaajan tietojen näyttämiseen, pistepörssin näyttämiseen ja yhden joukkueen pelaajien tietojen näyttämiseen
    • Player on luokka, jonka olioina Statistics käsittelee yksittäisen pelaajan tietoja
    • PlayerReader on luokka, jonka avulla ohjelma käy hakemassa pelaajien tiedot internetistä
  • Ohjelma on nyt ikävästi struktoroitu ja esim. yksikkötestaus on kovin hankalaa

itse tehtävä:

  • Määrittele rajapinta Reader, jolla on samat julkiset metodit kuin PlayerReaderilla, eli ainoastaan metodi List getPlayers(). Laita PlayerReader toteuttamaan rajapinta.
    • HUOM: NetBeansissa on automaattinen refaktorointiominaisuus, jonka avulla luokasta saa helposti generoitua rajapinnan, jolla on samat metodit kuin luokalla. Klikkaa luokan kohdalla hiiren oikeaa nappia, valitse refactor ja "extract interface"
  • Muokkaa ohjelman rakennetta siten, että Statictics saa konstruktoriparametrina Reader-tyyppisen olion.
  • Muokkaa pääohjelma siten, että se injektoi Statistics-oliolle PlayerReaderin ja kokeile että ohjelma toimii edelleen:
Statistics stats = new Statistics( new PlayerReader("http://nhlstats-2013-14.herokuapp.com/players.txt") );

3. NHLStatistics-ohjelman yksikkötestaus

  • tee yksikkötestit luokalle Statistics
    • testien kattavuuden (sekä instructions että branches) tulee (Statistics-luokan osalta) olla 100% (mitataan JaCoCo:lla, ks. viikon 1 tehtävä
    • testit eivät saa käyttää verkkoyhteyttä
    • verkkoyhteyden tarpeen saat eliminoitua luomalla testiä varten rajapinnan Reader-toteuttavan "stubin", jonka sisälle kovakoodaat palautettavan pelaajalistan
    • voit luoda stubin testin sisälle anonyyminä sisäluokkana seuraavasti:
public class StaticsticsTest {
 
    Reader readerStub = new Reader() {
 
        public List<Player> getPlayers() {
            ArrayList<Player> players = new ArrayList<Player>();
 
            players.add(new Player("Semenko", "EDM", 4, 12));
            players.add(new Player("Lemieux", "PIT", 45, 54));
            players.add(new Player("Kurri",   "EDM", 37, 53));
            players.add(new Player("Yzerman", "DET", 42, 56));
            players.add(new Player("Gretzky", "EDM", 35, 89));
 
            return players;
        }
    };
 
    Statistics stats;

    @Before
    public void setUp(){
        // luodaan Staatistics-olio joka käyttää "stubia"
        stats = new Statistics(readerStub);
    }  
}

Kun injektoit readerStub-olion testissä Statistics-oliolle, palauttaa se aina saman pelaajalistan.

4. lisää git:iä: branchit

lue brancheja käsittelevät osuudet seuraavista: https://we.riseup.net/debian/git-development-howto ja http://www.ralfebert.de/tutorials/git/

  • jos haluat lukea hieman perusteellisemman selityksen asiasta, lue http://git-scm.com/book:n luku kolme
  • tee samalla kaikki tekstien esimerkit

Kannattaa huomioida myös erittäin hyvä brancheja käsittelevä visuaalinen materiaali osoitteessa http://pcottle.github.com/learnGitBranching/

Varsin selkeältä vaikuttaa myös https://www.atlassian.com/git/tutorials/using-branches huom: kun liikut branchien välillä kannattaa pitää working tree ja staging -alue tyhjinä!

tee seuraavat paikalliseen git-repositorioosi (kyseessä ei siis tarvitse olla tehtävien palautusrepositorio)

  • luo repositorio ja committaa masteriin tiedosto masteri1.txt
  • luo branch eka, siirry branchiin, luo sinne tiedosto eka.txt ja committaa
  • siirry takaisin master-branchiin, tiedoston eka.txt ei pitäisi nyt näkyä
  • lisää ja committaa masteriin tiedosto masteri2.txt
  • mene branchiin eka ja tarkasta, että masteriin lisätty tiedosto ei ole branchissa
  • lisää branchiin tavaraa, esim. tiedosto eka2.txt ja committaa
  • siirry takaisin master-branchiin
  • tarkasta että eka-branchiin lisätyt muutokset eivät ole masterissa
  • tarkastele komennolla gitk --all miltä repositorio ja branchit näyttävät (gitk toimii windowsilla ainakin Github for Windowsin Git Shellissä.)
    • jos käytät Macia voit halutessasi asentaa gitx:n joka on modernisoitu versio gitk-ohjelmasta
  • mergeä branchin eka sisältö masteriin
  • katso jälleen miltä näyttää gitk --all
  • tuhoa branchi eka

5. lisää git:iä: konflikti!

tee paikalliseen git-repoon seuraavat

  • lisää master-branchiin tiedosto tarkea.txt, kirjota sinne muutama rivi tekstiä ja committaa
  • tee uusi branchi toka, editoi tiedoston tarkea.txt loppua ja committaa
  • mene takaisin master-branchiin, editoi tiedoston tarkea.txt alkua ja committaa
  • mergeä branchin toka sisältö masteriin
    • katso tiedoston tarkea.txt-sisältöä, sen pitäisi sisältää nyt molemmissa brancheissa tehdyt muutokset
    • huom: jo tässä vaiheessa saattaa syntyä konflikti jos olet vahingossa muuttanut merkkejä väärästä kohtaa tiedostoa! Toimi tällöin ao. ohjeen mukaan.
  • lisää jotain tiedoston loppuun ja committaa
  • siirry branchiin toka
  • lisää jotain tiedoston tarkea.txt loppuun ja committaa
  • mergeä branchin master sisältö branchiin toka
    • nyt pitäisi aiheutua konflikti
  • ratkaise konflikti:
    • editoi tiedoston tarkea.txt sisältö haluamaksesi
    • ja toimi em. artikkelien ohjeen mukaan eli lisää konfliktoinut tiedosto staging-alueelle ja committoi

6. lisää git:iä: branchit ja GitHub

aloita lukemalla ProGit kirjasta luku Remote Branches

branch githubiin:

  • lisää tehtävien palauttamiseen käyttämäsi GitHub-reposition paikalliseen kopioon branchit haara1 ja haara2
  • mene branchiin haara1, lisää sinne tiedosto haara1.txt ja committaa
  • mene branchiin haara2, lisää sinne tiedosto haara2.txt ja committaa
  • pushaa uudet branchit GitHubiin
  • tarkastele GitHub-repositoria selaimella, varmista että branchit syntyvät ja niillä on haluttu sisältö

kloonaa GitHub-repositoriosta koneellesi toinen kopio

  • kuten huomaat, eivät branchit tule kloonattuun kopioon
  • tee paikalliseen kopioon branch joka "träkkää" GitHub:issa olevan projektisi branchia haara1 (ks. http://git-scm.com/book/en/Git-Branching-Remote-Branches kohta Tracking Branches)
  • lisää "träkkäävään" branchiin joku tiedosto, committaa ja pushaa branchi GitHubiin
  • tarkastele GitHub-repositoria selaimella, varmista että branchi päivittyy

mene GitHub-repon alkuperäiseen paikalliseen kopioon

  • mene branchiin haara1 ja pullaa muutokset GitHub:in vastaavasta branchista
    • huom: koska kyseessä ei ole "träkkäävä" branchi, joudut pullaamaan komennolla git pull origin haara1
  • mene branchiin haara2, lisää sitten tiedosto, committaa ja pushaa branchi GitHubiin
    • huom: koska kyseessä ei ole "träkkäävä" branchi, ei git push riitä vaan joudut määrittelemään branchin jonne push kohdistuu eli antamaan komennon git push origin haara2

mene jälleen toiseen kopioon

  • suorita komento git remote show origin
    • komento kertoo 'origin':issa eli githubissa olevien branchien ja paikallisten branchien suhteen
  • tee sinne GitHub:issa olevan projektisi branchia haara2 träkkäävä branch
  • suorita jälleen git remote show origin, mitä muutoksia huomaat?
  • tee branchiin muutoksia ja pushaa ne githubiin
    • huom: koska kyseessä träkkäävä branch, riittää git push
  • tarkastele GitHub-repositoria selaimella, varmista että branchi päivittyy

suorita vielä komento git remote show origin alkuperäisessä paikallisessa kopiossa

Branchien kanssa työskentely voi aluksi tuntua sekavalta varsinkin jos GitHub:issa on myös useita brancheja.

mihin brancheja käytetään?

Ohjelmistotimi voi käyttää Gitiä hyvin monella eri tyylillä. Artikkeli https://de.atlassian.com/git/tutorials/comparing-workflows esittelee muutamia erilaisia tapoja järjestellä tiimin gitin käyttöön liittyvä workflow. Yksi yleinen tapa branchien käyttöön ovat ns. featurebranchit:

The core idea behind the Feature Branch Workflow is that all feature development should take place in a dedicated branch instead of the master branch. This encapsulation makes it easy for multiple developers to work on a particular feature without disturbing the main codebase. It also means the master branch will never contain broken code, which is a huge advantage for continuous integration environments.

Jos kiinnostaa, lue lisää yo. dokumentista. Tai tästä https://www.javacodegeeks.com/2015/11/git-branching-strategies.html

7. epäajantasaisen kopion pushaaminen

Demonstroidaan usein esiintyvää tilannetta, jossa epäajantasaisen repositorion pushaaminen githubissa olevaan etärepositorioon epäonnistuu.

  • mene alkuperäiseen repositorion alkuperäisen kopion master haaraan, tee joku muutos, commitoi ja pushaa se githubiin
  • mene toisen kopion master-haaraan ja tee sinne joku muutos
  • commitoi ja pushaa muutos githubiin
  • kaikki ei kuitenkaan mene hyvin, seurauksena on seuraavantyylinen virheilmoitus:
mbp-18:ohtu-viikko1-2017 mluukkai$ git push
To git@github.com:mluukkai/ohtu-viikko1-2017.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:mluukkai/ohtu-viikko1-2017.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first merge the remote changes (e.g.,
hint: 'git pull') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
mbp-18:ohtu-viikko1-2017 mluukkai$ 

Virheen syynä on se, että githubissa oleva master-haara oli edellä paikallisen repositorion master-haaraa. Ongelma korjaantuu tekemällä ensin git pull, ratkaisemalla mahdolliset konfliktit ja pushaamalla sitten uudelleen.

  • eli toimi näin ja varmista, että tekemäsi muutokset menevät githubiin

8. riippuvuuksien injektointi osa 3: Verkkokauppa

Kurssirepositorion hakemistossa koodi/viikko2/Verkkokauppa1 on yksinkertaisen verkkokaupan ohjelmakoodi

  • tutustu koodiin, piirrä luokkakaavio ohjelman rakenteesta

  • ohjelman luokista Pankki, Varasto, Viitegeneraattori ja Kirjanpito ovat sellaisia, että niistä on tarkoitus olla olemassa vain yksi olio. Tälläisiä ainutkertaisia olioita sanotaan singletoneiksi. Koodissa singletonit ovat toteutettu "klassisella tavalla"

    • Singleton on GoF-kirjan yksi alkuperäisistä suunnittelumalleista, lue lisää singletoneista esim. täältä
    • Singleton ei ole erinäisistä syistä enää oikein muodissa, ja korvaamme sen seuraavassa tehtävässä
  • kuten huomaamme, on koodissa toivottoman paljon konkreettisia riippuvuuksia:

    • Varasto --> Kirjanpito
    • Pankki --> Kirjanpito
    • Kauppa --> Pankki
    • Kauppa --> Viitegeneraatori
    • Kauppa --> Varasto
  • Pura luokan Kauppa konkreettiset riippuvuudet yllämainittuihin luokkiin rajapintojen avulla

    • Riippuvuus luokkaan Ostoskori voi jäädä sillä se on ainoastaan luokan Kauppa sisäisesti käyttämä luokka ja täten varsin harmiton
    • HUOM: NetBeansissa on automaattinen refaktorointiominaisuus, jonka avulla luokasta saa helposti generoitua rajapinnan, jolla on samat metodit kuin luokalla. Klikkaa luokan kohdalla hiiren oikeaa nappia, valitse refactor ja "extract interface"
    • muut riippuvuudet jätetään vielä
  • Määrittele luokalle sopiva konstruktori, jotta voit injektoida riippuvuudet, konstruktorin parametrien tulee olla tyypiltään rajapintoja

  • Muokkaa pääohjelmasi, siten että se luo kaupan seuraavasti:

Kauppa kauppa = new Kauppa(Varasto.getInstance(), Pankki.getInstance(), Viitegeneraattori.getInstance() );

9. riippuvuuksien injektointi osa 4: ei enää singletoneja verkkokaupassa

  • singleton-suunnittelumallia pidetään osittain ongelmallisena, poistammekin edellisestä tehtävästä singletonit
  • poista kaikista luokista getInstance-metodit ja staattinen instance-muuttuja
    • joudut muuttamaan luokilla olevat private-konstruktorit julkisiksi
  • poista rajapintojen ja dependency injektionin avulla edellisen tehtävän jäljiltä jääneet riippuvuudet, eli
    • Varasto --> Kirjanpito
    • Pankki --> Kirjanpito
  • Muokkaa pääohjelmasi vastaamaan uutta tilannetta, eli suunilleen muotoon:
Kirjanpito kirjanpito      = new Kirjanpito();
Varasto varasto            = new Varasto(kirjanpito);
Pankki pankki              = new Pankki(kirjanpito);
Viitegeneraattori viitegen = new Viitegeneraattori();
Kauppa kauppa              = new Kauppa(varasto, pankki, viitegen);

Kuten huomaamme, alkaa kaupan konfigurointi olla aika vaivalloista...

10. Spring osa 1: Verkkokauppa siistiksi

Tulet todennäköisesti saamaan Springiä käyttäessäsi pitkiä ja kryptiseltä vaikuttavia virheilmoituksia. Lue virheilmoitusten stack trace huolellisesti läpi, yleensä se antaa viheitä siitä, missä vika on. Virheilmoitusten tulkitseminen ja virheiden etsiminen on yksi tärkeimpiä taitoja ohjelmistoalalla, se voi tuntua ikävältä, mutta oikoteitä ei ole. Usein googlailu ja stack owerflow auttavat, mutta kaikesta ei selviä pelkällä trial and error -menetelmällä. Usein käytettävän kirjaston toimintaa on ymmärrettävä jollain tasolla, jotta virheiden jäljitys onnistuu.

Itse käytän virheiden jäljityksessä ns. paranoidimoodia. Jos olen epävarma siitä mitä teen (liittyen koodiin tai konfigurointiin) ,testaan lähes jokaisen rivin jälkeen, että toiminnallisuus on halutun kaltainen. Varmin keino aiheuttaa paljon kriptisiä virheitä on se, että testaa koodia mahdollisimman harvoin. Tällöin virheiden jäljitys on vaikeaa, sillä lisättyjä rivejä saattaa olla paljon ja virheen aiheuttaja ei välttämättä ole ilmeinen. Tälläisissä tilanteissa kannattaa esim. kommentoida lisätty koodi pois ja palauttaa lisäykset rivi kerrallaan.

Spring tarjoaa pelastuksen käsillä olevaan tilanteeseen.

Lue nyt https://github.com/mluukkai/ohtu2017/blob/master/web/riippuvuuksien_injektointi_spring.md

Palataan sitten verkkokaupan pariin.

  • projektiin on konfiguroitu valmiiksi springin tarvitsemat riippuvuudet, konfiguraatiotiedosto spring-context.xml löytyy hakemiston src/main/resources alta (NetBeansissa tämä löytyy kohdan Other Sources -alta)
  • HUOM mahdolliset virheilmoitukset "org.springframework... package does not exist" katoavat kun buildaat projektin ensimmäisen kerran!
  • ota mallia yllä olevan linkin ohjeesta ja konfiguroi verkkokauppa Springin xml-muotoista konfiguraatiota siten, että kauppa-olion luominen onnistuu Springin avulla seuraavasti:
public static void main(String[] args) {
    ApplicationContext ctx = new FileSystemXmlApplicationContext("src/main/resources/spring-context.xml");
 
    Kauppa kauppa = ctx.getBean(Kauppa.class);
    //...
}

Kannattanee edetä tehtävässä pienin askelin siirtäen yksi luokka kerrallaan Springin hallinnoinnin alle.

11. Spring osa 2: Verkkokauppa siistiksi annotaatioilla

  • HUOM: älä tee tätä edellisen tehtävän päälle, tee projektista kopio

  • tai tee se erilliseen branchiin:

    • lue kohta branches osoitteesta http://www.ralfebert.de/tutorials/git/
    • tee tälle tehtävälle branch "annotaatiot"
    • jotta joku muu branch kuin master (eli "pääbranch") saadaan githubiin, tulee push-komennon olla muodossa git push origin
    • palaamme brancheihin tarkemmin ensi viikon tehtävissä
  • muuta edellistä tehtävää siten, että konfigurointi tapahtuu annotaatioiden @Component ja @Autowired avulla

  • huom:

    • tehtävää ei välttämättä kannata tehdä yhtenä isona askeleena, saattaa olla viisasta muuttaa luokka kerrallaan xml-konfiguraatiosta annotaatiokonfiguroiduksi
    • virheilmoitukset eivät ole noviisille selkeimpiä mahdollisa
    • muista määritellä @Component kaikkiin edellisessä tehtävässä xml:ssä määriteltyihin luokkiin
    • muista laittaa @Autowired jokaiseen luokkaan, jolla on riippuvuuksia

tehtävien kirjaaminen palautetuksi

tehtävien kirjaus:

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

palaute tehtävistä:

  • Lisää viikon 1 tehtävässä 12 forkaamasi repositorion omalla nimelläsi olevaan hakemistoon tiedosto nimeltä viikko2
  • 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