-
Notifications
You must be signed in to change notification settings - Fork 67
laskari 2
Tehtävien palautuksen deadline su 3.4. klo 23.59 Huom: pääsiaäisloma 24-30.3. ohjausta tehtävien tekoon to 31.3. 14-17 B221
- 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
- lue ensin tiistain luennolla nopeasti läpikäytyjen asioiden kertaus https://github.com/mluukkai/ohtu2016/blob/master/web/riippuvuuksien_injektointi.md
- hae koodiesimerkit repostitoriosta https://github.com/mluukkai/ohtu2016/ (hakemistosta viikko2/RiippuvuuksienInjektointi) ja kokeile että kaikki toimivat
- järkevintä lienee että kloonaat repositorion paikalliselle koneellesi
- vaikka viime viikolla sama repositorio forkattiin, ei forkattua repositorioa saa ihan helposti synkronoitua alkuperäiseen
-
repositorion https://github.com/mluukkai/ohtu2016/ hakemistossa viikko2/Ohtu-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 metodiList 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") );
- tee yksikkötestit luokalle Statistics
- testien rivi- ja haarautumakattavuuden tulee (Statistics-luokan osalta) olla 100% (mitataan coberturalla, ks. viikko 1)
- 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 {
Statistics stats;
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;
}
};
// ...
}
Kun injektoit readerStub-olion testissä Statistics-oliolle, palauttaa se aina saman pelaajalistan.
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/tutorial/git-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
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
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
- huom: koska kyseessä ei ole "träkkäävä" branchi, joudut pullaamaan komennolla
- 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
- 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
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.
Ohjelmistotimi voi käyttää Gitiä hyvin monella eri tyylillä. Artikkeli https://www.atlassian.com/git/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
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-2016 mluukkai$ git push To [email protected]:mluukkai/ohtu-viikko1-2016.git ! [rejected] master -> master (fetch first) error: failed to push some refs to '[email protected]:mluukkai/ohtu-viikko1-2016.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-2016 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
Repositorion https://github.com/mluukkai/ohtu2016/ hakemistossa viikko2/Verkkokauppa1 on yksinkertaisen verkkokaupan ohjelmakoodi
-
tutustu koodiin, piirrä luokkakaavio ohjelman rakenteesta
-
ohjelman luokista
Pankki
,Varasto
,Viitegeneraattori
jaKirjanpito
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 rajapintojen avulla- 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 seuraavaan tyyliin:
Kauppa kauppa = new Kauppa(Varasto.getInstance(), Pankki.getInstance(), Viitegeneraattori.getInstance() );
- singleton-suunnittelumallia pidetään osittain ongelmallisena, poistammekin edellisestä tehtävästä singletonit
-
poista kaikista luokista
getInstance
-metodit ja staattineninstance
-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...
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 sivu Riippuvuuksien-injektointi kohdasta Dependency injection Spring-sovelluskehyksessä loppuun asti
- 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 tehtävän 1 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
-
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 viistasta 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 kirjaus:
- Kirjaa tekemäsi tehtävät tänne
- huom: tehtävien palautuksen deadline on su 3.4. klo 23.59
palaute tehtävistä:
- Lisää viikon 1 tehtävässä 11 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