*Allaolevien tehtävien deadline on *

Apua tehtävien tekoon kurssin Discord-kanavalla, kampuksella ja Zoomissa:

  • Maanantaisin (poislukien 6.12.) klo 14-16 kampuksella BK107-luokassa
  • Torstaisin (4.11. alkaen) klo 14-16 Zoomissa

Ohjauskalenteri:

Muista myös tämän viikon monivalintatehtävät, joiden deadline on .

Viikon ensimmäisessä tehtävässä tutustutaan tarkemmin gradleen. Toinen ja kolmas tehtävä käsittelevät koodin staattisen analyysin työkalua checkstyleä. Gitiin tutustuminen jatkuu tehtävissä 4-8. Laskarien lopuksi jatketaan riippuvuuksien injektoinnin parissa.

Typoja tai epäselvyyksiä tehtävissä?

Tee korjausehdotus editoimalla tätä tiedostoa GitHubissa.

Tehtävien palauttaminen

Osa git-tehtävistä (tehtävät 4-6) tehdään ainoastaan paikalliseen repositorioon, eli ne eivät näy palautuksessa mitenkään.

Muut tehtävät palautetaan GitHubiin, sekä merkitsemällä tehdyt tehtävät palautussovellukseen https://study.cs.helsinki.fi/stats/courses/ohtu-avoin-2022

Tehtävät 2 ja 3 laajentavat viime viikon ensimmäistä tehtäväsarjaa, eli ne palautetaan samaan repositorioon kuin Ohtuvarasto. Muut tehtävät voit palauttaa samaan repositorioon mihin palautit ensimmäisen viikon tehtävät 14-16.

Katso tarkempi ohje palautusrepositorioita koskien täältä.

Huomio gradleen liittyen

Käytämme tälläkin viikolla gradle-muotoisia projekteja. Jos gradle-koodi lukee syötteitä komentoriviltä, tulee määrittelytiedostojen 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äärittely on jo lisätty.

Jos ohjelma lukee syötteitä käyttäjältä, kannattaa se suorittaa komennolla gradle -q --console plain run, jolloin gradlen tekemät tulostukset eivät tule konsoliin.

HUOM! näyttää siltä, että NetBeans 11.1:llä Scanner ei toimi ollenkaan gradle-projekteissa, eli jos törmäät samaan ongelmaan, suorita ohjelmat komentoriviltä.

1. gradlen perusteita

Olemme jo käyttäneet gradlea hyvällä menestyksellä viikon ajan. Tutustutaan nyt gradleen hieman tarkemmin tekemällä täällä oleva interaktiivinen “tutoriaali”.

2. lisää gradlea: koodin staattinen analyysi

Kurssin kolmannessa osassa teemana on ohjelmien laadun varmistaminen. Eräs ohjelman laatua useimmiten edistävä tekijä on järkevän koodityylin noudattaminen. Koodin tyyliä voidaan tarkkailla automatisoidusti ns. staattisen analyysin työkaluilla.

Tutustutaan nyt staattisen analyysin työkaluun checkstyleen, jonka käyttäminen on suhteellisen vaivatonta gradlen checkstyle-pluginin avulla.

Mene nyt viikon 1 tehtävien Ohtuvarastoon liittyvien tehtävien palautusrepositorioosi.

Lisää projektiin checkstyle-plugin tämän ohjeen mukaan, eli muuttamalla konfiguraationtiedoston build.gradle alkuosaa seuraavasti:

plugins {
    id 'application'
    id 'jacoco'
    id 'checkstyle'
}

Suorita komento gradle checkstyleMain

Suoritus epäonnistuu, virheilmoitus kertoo mistä kyse:

* What went wrong:
Execution failed for task ':checkstyleMain'.
> Unable to create Root Module: config {/Users/mluukkai/dev/ohtu-poc/config/checkstyle/checkstyle.xml}, classpath {/Users/mluukkai/dev/ohtu-poc/build/classes/java/main:/Users/mluukkai/dev/ohtu-poc/build/resources/main:/Users/mluukkai/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/29.

Eli kuten manuaali kertoo, Gradle olettaa että projektista löytyy checkstylen toiminnan määrittelevä konfiguraatiotiedosto checkstyle.xml joka tulee sijoittaa projektin juuren alle tehtävään hakemistoon config/checkstyle.

Luo tiedosto, ja hae tiedostolle sisältö täältä.

Huomaa, että tiedoston tulee olla oikeassa paikassa. Virheilmoitus ja manuaali kertovat oikean sijainnin!

Kun nyt suoritat komennon gradle checkstyleMain, tulee jälleen virhe, mutta nyt virheen syynä on se, että koodi rikkoo konfiguraatiotiedostossa määriteltyjä tyylisääntöjä. Virheilmoitus kertoo raportin sijainnin:

* What went wrong:
Execution failed for task ':checkstyleMain'.
> Checkstyle rule violations were found. See the report at: file:///Users/mluukkai/opetus/ohtu2020/ohtu-2021-viikko1/build/reports/checkstyle/main.html

Avaa raportti selaimella. Huomaat, että tuloksena on suuri määrä virheitä. Valitettavasti virheraportti kertoo ainoastaan sen koodirivin, mistä virhe löytyy. Joudut katsomaan vastaavan kohdan koodistasi esim. NetBeansista.

Toimi nyt seuraavasti

  • Poista checkstylen konfiguraatiotiedostosta kaikki määritellyt säännöt siten, että sen sisällöksi jää vain
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">

</module>
  • Suorita gradle checkstyleMain ja varmista, että tarkastus menee läpi
  • Määrittele nyt tiedostoon seuraavat säännöt (ks. kohta checks checkstylen sivuilta):
    • metodien pituus max 15 riviä (tämä ja seuraavat säännöt määritellään moduulin tree walker sisälle)
    • ei yli yhtä sisäkkäisiä if-komentoja
    • ei sisäkkäisiä for-komentoja, seuraavan siis pitäisi aiheuttaa virhe:
      for( int i=0; i<1; i++ ) {
        for( int j=0; i<j; j++ ) {
          System.out.println("virhe");
        } 
      }
      
    • koodi on oikein sisennettyä
    • lohkon avaava aaltosulku on aina rivin lopussa, eli esim. ehtolauseissa aaltosulku tulee merkitä
      if ( ehto ) 
      {
        System.out.println("virhe");
      }
      

      sijaan seuraavasti:

      if ( ehto ) {
        System.out.println("virhe");
      }
      
    • syklomaattinen koodikompleksisuus korkeintaan 3 (selvitä mitä tarkoittaa!)
  • Muuta koodiasi siten, että saat jokaisen määritellyistä checkstyle-säännöistä rikkoutumaan
  • Korjaa koodisi ja varmista, että se noudattaa kaikkia sääntöjä
    • pääohjelman koodin voi poistaa tarvittaessa kokonaan, jotta saat koodin säännönmukaiseksi

3. Koodin staattinen analyysi ja GitHub Actionit

Laajenna projektisi GitHub Actionien määritelmää siten, että myös checkstyle-tarkastukset suoritetaan aina kun koodi pushataan GitHubiin.

Varmista, että GitHub huomaa tilanteen, missä koodi rikkoo projektin checkstyle-sääntöjä:

Varmista myös, että kun korjaat koodin, kaikki toimii taas moitteettomasti:

4. git: branchit

lue brancheja käsittelevät osuudet seuraavasta https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging

  • jos haluat lukea hieman perusteellisemman selityksen asiasta, lue <https://git-scm.com/book/en/v2:n luku kolme kokonaisuudessaan

Kannattaa huomioida myös erittäin hyvä brancheja käsittelevä visuaalinen materiaali osoitteessa https://learngitbranching.js.org/

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 mainiin tiedosto main1.txt
  • luo branch eka, siirry branchiin, luo sinne tiedosto eka.txt ja committaa
  • siirry takaisin main-branchiin, tiedoston eka.txt ei pitäisi nyt näkyä
    • huom: muistutus vielä siitä, että kun siirryt branchista toiseen varmista aina komennolla git status että kaikki muutokset on committoitu
  • lisää ja committaa mainiin tiedosto maini2.txt
  • mene branchiin eka ja tarkasta, että mainiin lisätty tiedosto ei ole branchissa
  • lisää branchiin tavaraa, esim. tiedosto eka2.txt ja committaa
  • siirry takaisin main-branchiin
  • tarkasta että eka-branchiin lisätyt muutokset eivät ole mainissa
  • tarkastele komennolla gitk --all miltä repositorio ja branchit näyttävät (gitk toimii windowsilla ainakin Github for Windowsin Git Shellissä.)
    • gitk ei toimi maceissa, hyvä korvaaja sille on sourcetree
  • mergeä branchin eka sisältö mainiin
  • katso jälleen miltä näyttää gitk –all

5. git: branchit ja staging-alue (versionhallinta)

  • olet nyt repositoriosi main-haarassa
  • luo uusi tiedosto uusi_tiedosto.txt, älä kuitenkaan lisää ja commitoi tiedostoa
  • komennon git status tulostuksen pitäisi olla seuraava
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)

	uusi_tiedosto.txt

nothing added to commit but untracked files present (use "git add" to track)
  • siirry nyt branchiin eka
  • suorita uudelleen komento git status
  • huomaat, että tulostus on edelleen sama, tiedosto ei edelleenkään ole versionhallinnan alla
  • eli vaikka olit main-haarassa kun loit tiedoston, ei main-haara eikä koko git tiedä tiedostosta vielä mitään ennen kuin lisäät sen versionhallinnan alaisuuteen komennolla git add
  • lisää tiedosto nyt versionhallinnan alaisuuteen ja commitoi se
  • tiedosto menee nykyiseen branchiisi, eli branchiin eka, main ei edelleenkään tiedä tiedostosta mitään
  • luo uusi tiedosto uusi_tiedosto2.txt ja lisää se versionhallintaan, älä kuitenkaan commitoi
  • tarkasta että komennon git status tulos on
On branch eka
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   uusi_tiedosto2.txt
  • olet siis branchissa eka ja uusi_tiedosto2.txt on lisätty staging-alueelle, sitä ei kuitenkaan ole vielä committoitu
  • siirry nyt branchiin main
  • komennon git status tulos on edelleen sama, uusi_tiedosto2.txt on edelleen staging-alueella mutta committoimattomana
  • staging-alue ei kuulu mihinkään branchiin, eli jos staging-alueella on committoimattomia muutoksia ja vaihdat branchia, säilyvät samat asiat stagingissa
  • muutokset siirtyvät stagingista branchiin ainoastaan komennolla git commit
  • committoi nyt staging-alueen muutokset eli uusi_tiedosto2.txt mainiin
  • komennon git status tulos kertoo nyt että staging-alue on tyhjä:
On branch main
nothing to commit, working tree clean
  • siirry jälleen branchiin eka ja huomaat, että uusi_tiedosto2.txt ei ole olemassa
  • mergeä main branchiin eka
  • siirry nyt mainiin ja tuhoa branchi eka
  • tämän tehtävän ideana oli siis havainnollistaa, että working tree (muutokset joista git ei ole tietoinen) ja staging (gitiin lisättyihin tiedostoihin tehdyt committoimattomat muutokset) eivät liity mihinkään branchiin, muutokset siirtyvät staging-alueelta branchiin ainoastaan komennon git commit suorituksen seurauksena

6. git: konflikti! (versionhallinta)

Tee paikalliseen git-repoon seuraavat

  • lisää main-branchiin tiedosto tarkea.txt, kirjota sinne muutama rivi tekstiä ja committaa
  • tee uusi branchi toka, mene branchiin ja editoi tiedoston tarkea.txt loppua (lisää esim loppuun muutama uusi rivi) ja committaa
  • mene takaisin main-branchiin, editoi tiedoston tarkea.txt alkua (lisää alkuun muutama rivi) ja committaa
  • mergeä branchin toka sisältö mainiin
    • mergeäminen aiheuttaa ns merge-commitin, ja avaa tekstieditorin mihin joudut kirjoittamaan commit-viestin
      • jos et ole määritellyt gitille editoria viime viikon tehtävän 2 ohjeiden mukaan, avautuu ehkä gitin oletusarvoinen editori vim
      • vimistä poistuminen saattaa osoittautua ensikertalaiselle hankalaksi, google auttaa tarvittaessa
    • 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 main sisältö branchiin toka
    • nyt pitäisi aiheutua konflikti, komento aiheuttaa tulostuksen
      Auto-merging tarkea.txt
      CONFLICT (content): Merge conflict in tarkea.txt
      Automatic merge failed; fix conflicts and then commit the result.
      
  • ratkaise konflikti:
    • editoi tiedoston tarkea.txt sisältö haluamaksesi
    • ja toimi em. artikkelien ohjeen mukaan eli lisää konfliktoinut tiedosto staging-alueelle ja committoi

Jotkut editorit, esim visual studio code sisältävät sisäänrakennetusti ns. merge toolin, joka osaa jossain määrin helpottaa konfliktien ratkaisua:

7. git: branchit ja GitHub (versionhallinta)

Aloita lukemalla ProGit kirjasta luku Remote Branches

Lisätään seuraavaksi branch githubiin:

  • lisää tehtävien palauttamiseen käyttämäsi GitHub-repositorion 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-repositoriota 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-repositoriota 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
    • koska kyseessä ei ole “träkkäävä” branchi, ei vanhemmilla gitin versiolla komento git push riitä vaan joudut määrittelemään branchin jonne push kohdistuu eli antamaan komennon git push origin haara2
    • uudemmilla gitin versioilla pushaus onnistuu suoraan

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ä on träkkäävä branch, riittää git push
  • tarkastele GitHub-repositoriota selaimella, varmista että branchi päivittyy

Palaa vielä alkuperäiseen lokaaliin repositorioon

  • suorita komento git remote show origin
  • tulostus kertoo, että lokaaleista haaroista ainoastaan main on konfiguroitu komennon git pull osalta, eli on träkkäävä branchi:
* remote origin
  Fetch URL: git@github.com:mluukkai/ohtu-2021-viikko1.git
  Push  URL: git@github.com:mluukkai/ohtu-2021-viikko1.git
  HEAD branch: main
  Remote branches:
    haara1 tracked
    haara2 tracked
    main tracked
  Local branch configured for 'git pull':
    main merges with remote main
  Local refs configured for 'git push':
    haara1 pushes to haara1 (up to date)
    haara2 pushes to haara2 (up to date)
    main pushes to main (up to date)
  • suorita git pull branchissä haara1
  • komennon tuloste antaa ohjeen, miten saat konfiguroitua haara1:n träkkäämään githubissa olevaa haaraa:
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> haara1
 
  • Kun annat komennon, sen jälkeen haara träkkää githubissa olevaa haaraa ja komento git pull voidaan antaa ilman parametreja

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

Mihin brancheja käytetään?

Ohjelmistotiimi voi soveltaa Gitin branchaystä hyvin monella eri tyylillä. Artikkeli https://www.atlassian.com/git/tutorials/comparing-workflows esittele tähän muutamia vaihtoehtoja. Eräs 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 main branch. This encapsulation makes it easy for multiple developers to work on a particular feature without disturbing the main codebase. It also means the main branch will never contain broken code, which is a huge advantage for continuous integration environments.

Jos kiinnostaa, lue lisää yo. dokumentista.

8. git: epäajantasaisen kopion pushaaminen (versionhallinta)

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

  • mene alkuperäisen repositorion paikallisen kopion main -haaraan, tee joku muutos, commitoi ja pushaa se githubiin
  • mene toisen kopion main-haaraan ja tee sinne joku muutos
  • commitoi ja pushaa muutos githubiin
  • kaikki ei kuitenkaan mene hyvin, seurauksena on seuraavantyylinen virheilmoitus:
$ git push
To git@github.com:mluukkai/ohtu-viikko1-2020.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'git@github.com:mluukkai/ohtu-viikko1-2020.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.
$ 

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

  • komennon git pull yhteydessä syntyy merge-commit, ja avautuu tekstieditori mihin joudut kirjoittamaan commit-viestin
  • eli toimi näin ja varmista, että tekemäsi muutokset menevät githubiin

9. riippuvuuksien injektointi osa 3: Verkkokauppa (versionhallinta)

Tutustuimme viime viikon tehtävissä 14-16 riippuvuuksien injektointiin ja sovelsimme sitä yksikkötestauksen helpottamiseen.

Jos asia on päässyt unohtumaan, voit kerrata asian lukemalla tämän.

Kurssirepositorion hakemistossa koodi/viikko2/Verkkokauppa1 on yksinkertaisen verkkokaupan ohjelmakoodi

  • Hae projekti kurssirepositoriosta
    • järkevintä lienee että kloonaat kurssirepositorion paikalliselle koneellesi jos et ole sitä jo tehnyt, jos olet, niin pullaa repositorio ajantasalle
    • tämän jälkeen kannattaa kopioida projekti tehtävien palautukseen käyttämäsi repositorion sisälle
  • Tutustu koodiin, piirrä luokkakaavio ohjelman rakenteesta
    • luokkakaavioita ei tarvitse palauttaa…
  • Ohjelman luokista Pankki, Varasto, Viitegeneraattori ja Kirjanpito ovat sellaisia, että niistä on tarkoitus olla olemassa ainoastaan yksi olio. Tälläisiä ainutkertaisia olioita sanotaan singletoneiksi. Koodissa singletonit ovat toteutettu “klassisella tavalla”, eli piilottamalla konstruktori ja käyttämällä staattista muuttujaa ja metodia säilömään ja palauttamaan luokan ainoa olio
    • Singleton on ns. 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
  • Poista 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 Kauppa sopiva konstruktori, jotta voit injektoida riippuvuudet, konstruktorin parametrien tulee olla tyypiltään rajapintoja
  • Älä käytä luokan Kauppa sisällä enää konkreettisia luokkia Varasto, Viitegeneraattori ja Pankki vaan ainoastaan niitä vastaavia rajapintoja!
  • Muokkaa pääohjelmasi, siten että se luo kaupan seuraavasti:
Kauppa kauppa = new Kauppa(
  Varasto.getInstance(), 
  Pankki.getInstance(), 
  Viitegeneraattori.getInstance() 
);
  • Varmista ohjelman toimivuus suorittamalla se komentoriviltä komennolla gradle run

10. 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 riippuvuuksien injektoinnin avulla edellisen tehtävän jäljiltä jääneet riippuvuudet, eli
    • Varasto –> Kirjanpito
    • Pankki –> Kirjanpito
  • Muokkaa pääohjelmasi vastaamaan uutta tilannetta, eli suunnilleen muotoon:
Viitegeneraattori viitegen = new Viitegeneraattori();
Kirjanpito kirjanpito      = new Kirjanpito();
Varasto varasto            = new Varasto(kirjanpito);
Pankki pankki              = new Pankki(kirjanpito);
Kauppa kauppa              = new Kauppa(varasto, pankki, viitegen);

Kuten huomaamme, alkaa kaupan konfigurointi olla aika vaivalloista…

11 Spring osa 1: riippuvuuksien injektointi

Kurssilla Web-palvelinohjelmointi käytettävä Spring-sovelluskehys tarjoaa pelastuksen käsillä olevaan tilanteeseen.

Lue nyt täällä oleva kuvaus miten riippuvuuksien injektointi voidaan automatisoida Springillä

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 vihjeitä 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 overflow 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 kryptisiä 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.

12 Spring osa 2: Verkkokauppa siistiksi

Palataan sitten verkkokaupan pariin.

  • Projektiin on konfiguroitu valmiiksi springin tarvitsemat riippuvuudet, sekä konfiguraatiot
    • HUOM! mahdolliset virheilmoitukset “org.springframework… package does not exist” katoavat kun buildaat projektin ensimmäisen kerran!
  • Ota riippuvuuksien injektointi käyttöön lisäämällä luokille annotaatioita @Component ja @Autowired
  • Aloita muuttamalla pääohjelma siten, että ainoastaan viitegeneraattori luodaan Springin avulla. Muutos on suunnilleen seuraava:
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
 
    Viitegeneraattori viitegen = ctx.getBean(Viitegeneraattori.class);
    Kirjanpito kirjanpito      = new Kirjanpito();
    Varasto varasto            = new Varasto(kirjanpito);
    Pankki pankki              = new Pankki(kirjanpito);
    Kauppa kauppa              = new Kauppa(varasto, pankki, viitegen);

    //...
}
  • etene seuraavaan luokkaan:
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
 
    Viitegeneraattori viitegen = ctx.getBean(Viitegeneraattori.class);
    Kirjanpito kirjanpito      = ctx.getBean(Kirjanpito.class);
    Varasto varasto            = new Varasto(kirjanpito);
    Pankki pankki              = new Pankki(kirjanpito);
    Kauppa kauppa              = new Kauppa(varasto, pankki, viitegen);

    //...
}
  • ja seuraavaan:
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
 
    Viitegeneraattori viitegen = ctx.getBean(Viitegeneraattori.class);
    Kirjanpito kirjanpito      = ctx.getBean(Kirjanpito.class);
    Varasto varasto            = ctx.getBean(Varasto.class);
    Pankki pankki              = new Pankki(kirjanpito);
    Kauppa kauppa              = new Kauppa(varasto, pankki, viitegen);

    //...
}

* Muuta sitten loputkin luokat käyttämään Springin riippuvuuksien injektointia, jolloin pääohjelman alku muuttuu muotoon: 

``` java
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
 
    Kauppa kauppa = ctx.getBean(Kauppa.class);
    // ...
}
  • Huom: pääohjelma tarvitsee kaupan lisäksi kirjanpito-olioa lopun tulostuksessa, sen saa haltuunsa Springin kontekstilta metodikutsulla ctx.getBean(Kirjanpito.class)

Tehtävien palautus

Pushaa kaikki tekemäsi tehtävät (paitsi ne, joissa mainitaan, että tehtävää ei palauteta mihinkään) GitHubiin ja merkkaa tekemäsi tehtävät palautussovellukseen https://study.cs.helsinki.fi/stats/courses/ohtu-avoin-2022