Apua tehtävien tekoon kurssin Discord-kanavalla sekä zoom-pajassa:
- Torstaisin klo 14-16 tällä linkillä
Tehtävissä 1–2 jatketaan gitin harjoittelua. Tehtävät 1 ja 2 eivät näy palautuksissa mitenkään.
Tehtävät 3–5 liittyvät materiaalin ohjelmistosuunnittelua käsittelevän osan 4 niihin lukuihin, joihin on merkitty [viikko 5].
Tehtävä 6 käsittelee retrospektiivitekniikoita.
Typoja tai epäselvyyksiä tehtävissä?
Tee korjausehdotus editoimalla tätä tiedostoa GitHubissa.
Tehtävien palauttaminen
Tehtävät palautetaan GitHubiin ja merkitsemällä tehdyt tehtävät palautussovellukseen https://study.cs.helsinki.fi/stats/courses/ohtu-avoin-2022.
Katso tarkempi ohje palautusrepositorioita koskien täältä.
1. git: vahingossa tuhotun tiedoston palautus (versionhallinta)
Edellisessä tehtävässä palasimme jo menneisyyteen checkouttaamalla tagillä merkittyyn kohtaan. Katsotaan nyt miten voimme palauttaa jonkun menneisyydessä olevan tilanteen uudelleen voimaan.
Voit tehdä tämän ja seuraavan tehtävän mihin tahansa repositorioon, tehtävät eivät näy palautuksissa.
- Tee jokin tiedosto, esim. nimeltään xxx, lisää ja committaa se.
- Poista tiedosto ja committaa.
- Tee jotain muutoksia johonkin muuhun tiedostoon ja committaa.
- Historiasi näyttää seuraavalta:
(1) - (2) - (3)
- Nykyhetki eli HEAD on (3). Commitissa (1) tiedosto xxx on olemassa nykyhetkellä ja (2):ssa xxx:ää ei ole. Historiaa voit tutkia komennoilla
git log
jagitk
. - Seuraavaksi palautetaan tiedosto xxx.
- Aloita selvittämällä sen commitin id, jossa tiedosto xxx vielä on olemassa. Tämä onnistuu gitk:lla tai
git log
-komennolla (tai jos haluaa päästä helpolla, komennollagit log --pretty=oneline --name-status
). - Jatka palauttamalla tiedosto xxx komennolla
git checkout id -- xxx
, jossaid
on edellisessä vaiheessa selvittämäsi commitin id. - Varmista lopuksi tiedoston xxx ilmestyneen staging-alueelle komennolla
git status
. - Lisää vielä tiedoston palauttamisen jälkeen muuttunut tilanne versiohistoriaan komennoilla
git add
jagit commit
.
Huom: Koko id:tä ei komennossa tarvitse antaa. Voit antaa alusta niin monta merkkiä, että niiden perusteella id voidaan päätellä yksikäsitteisesti repositoriosi historiassa:
“Generally, eight to ten characters are more than enough to be unique within a project. For example, as of October 2017, the Linux kernel (which is a fairly sizable project) has over 700,000 commits and almost six million objects, with no two objects whose SHA-1s are identical in the first 11 characters.” 7.1 Git Tools - Revision Selection
Samalla tavalla onnistuu olemassa olevan tiedoston vanhan version palauttaminen.
2. Git: commitin muutosten kumoaminen (versionhallinta)
- Huomaamme, että juuri tehty commit oli virhe, joten kumotaan se sanomalla
git revert HEAD --no-edit
. KomennossaHEAD
viittaa committiin, jonka kohdalla nyt ollaan (ns. “nykytilanne” versiohistoriassa). - Syntyy uusi commit, jossa edellisessä tehdyt muutokset on kumottu.
Ilman komentorivivalitsinta –no-edit pääset muokkaamaan kumoamiseen liittyvään commitiin tulevaa viestiä. Ja komennolla git checkout HEAD^
pääsemme takaisin kumottuun tilanteeseen, eli mitään ei ole lopullisesti kadotettu.
Vastaavalla tavalla voidaan revertata mikä tahansa commit, eli: git revert id
, jossa id
on kumottavan commit:n id.
3. Tenniksen pisteenlaskun refaktorointi
Kurssirepositorion hakemistosta koodi/viikko5/tennis löytyy ohjelma, joka on tarkoitettu tenniksen pisteenlaskentaan.
Pisteenlaskennan rajapinta on yksinkertainen. Metodi get_score
kertoo voimassa olevan tilanteen tenniksessä käytetyn pisteenlaskennan määrittelemän tavan mukaan. Sitä mukaa kun jompikumpi pelaajista voittaa palloja, kutsutaan metodia won_point
, jossa parametrina on pallon voittanut pelaaja.
Esim. käytettäessä pisteenlaskentaa seuraavasti:
game = TennisGame("player1", "player2")
print(game.get_score())
game.won_point("player1")
print(game.get_score())
game.won_point("player1")
print(game.get_score())
game.won_point("player2")
print(game.get_score())
game.won_point("player1")
print(game.get_score())
game.won_point("player1")
print(game.get_score())
tulostuu
Love-All
Fifteen-Love
Thirty-Love
Thirty-Fifteen
Forty-Fifteen
Win for player1
Tulostuksessa siis kerrotaan pelitilanne kunkin pallon jälkeen kun player1 voittaa ensimmäiset 2 palloa, player2 kolmannen pallon ja player1 loput 2 palloa.
Pisteenlaskentaohjelman koodi toimii ja sillä on erittäin kattavat testit. Koodi on kuitenkin sisäiseltä laadultaan kelvotonta.
Tehtävänä on refaktoroida koodi luettavuudeltaan mahdollisimman ymmärrettäväksi. Koodissa tulee välttää “taikanumeroita” ja huonosti nimettyjä muuttujia. Koodi kannattaa jakaa moniin pieniin metodeihin, jotka nimennällään paljastavat oman toimintalogiikkansa.
Etene refaktoroinnissa todella pienin askelin. Suorita testejä mahdollisimman usein. Yritä pitää ohjelma koko ajan toimintakunnossa.
Jos haluat käyttää jotain muuta kieltä kuin Pythonia, löytyy koodista ja testeistä versioita useilla eri kielillä osoitteesta https://github.com/emilybache/Tennis-Refactoring-Kata
Tehtävä on kenties hauskinta tehdä pariohjelmoiden. Itse tutustuin tehtävään kesällä 2013 Extreme Programming -konferenssissa järjestetyssä Coding Dojossa, jossa tehtävä tehtiin satunnaisesti valitun parin kanssa pariohjelmoiden.
Lisää samantapaisia refaktorointitehtäviä löytyy Emily Bachen GitHubista.
4. Laskin ja komento-oliot
Kurssirepositorion hakemistoissa koodi/viikko5/laskin, löytyy yksinkertaisen laskimen toteutus. Laskimelle on toteutettu graafinen käyttöliittymä Tkinter-kirjaston avulla. Tarvittaessa lue ensin kurssin Ohjelmistotekniikka materiaalissa oleva tkinter-tutoriaali.
Asenna projektin riippuvuudet komennolla poetry install
ja käynnistä laskin virtuaaliympäristössä komennolla python3 src/index.py
. Komennon suorittamisen tulisi avata ikkuna, jossa on laskimen käyttöliittymä.
Sovelluksen avulla pystyy tällä hetkellä tekemään yhteen- ja vähennyslaskuja, sekä nollaamaan laskimen arvon. Laskutoimituksen kumoamista varten on lisätty jo painike “Kumoa”, joka ei vielä toistaiseksi tee mitään. Sovelluksen varsinainen toimintalogiikka on luokassa Kayttoliittyma
. Koodissa on tällä hetkellä hieman ikävä if
-hässäkkä:
def _suorita_komento(self, komento):
arvo = 0
try:
arvo = int(self._syote_kentta.get())
except Exception:
pass
if komento == Komento.SUMMA:
self._sovelluslogiikka.plus(arvo)
elif komento == Komento.EROTUS:
self._sovelluslogiikka.miinus(arvo)
elif komento == Komento.NOLLAUS:
self._sovelluslogiikka.nollaa()
elif komento == Komento.KUMOA:
pass
self._kumoa_painike["state"] = constants.NORMAL
if self._sovelluslogiikka.tulos == 0:
self._nollaus_painike["state"] = constants.DISABLED
else:
self._nollaus_painike["state"] = constants.NORMAL
self._syote_kentta.delete(0, constants.END)
self._tulos_var.set(self._sovelluslogiikka.tulos)
Refaktoroi koodi niin, ettei _suorita_komento
-metodi sisällä pitkää if
-hässäkkää. Hyödynnä kurssimateriaalin osassa 4 esiteltyä suunnittelumallia command.
Tässä tehtävässä ei tarvitse vielä toteuttaa kumoa-komennon toiminnallisuutta!
Luokka Kayttoliittyma
voi näyttää refaktoroituna esimerkiksi seuraavalta:
class Komento(Enum):
SUMMA = 1
EROTUS = 2
NOLLAUS = 3
KUMOA = 4
class Kayttoliittyma:
def __init__(self, sovelluslogiikka, root):
self._sovelluslogiikka = sovelluslogiikka
self._root = root
self._komennot = {
Komento.SUMMA: Summa(sovelluslogiikka, self._lue_syote),
Komento.EROTUS: Erotus(sovelluslogiikka, self._lue_syote),
Komento.NOLLAUS: Nollaus(sovelluslogiikka, self._lue_syote),
Komento.KUMOA: Kumoa(sovelluslogiikka, self._lue_syote)
}
# ...
def _lue_syote(self):
return self._syote_kentta.get()
def _suorita_komento(self, komento):
komento_olio = self._komennot[komento]
komento_olio.suorita()
self._kumoa_painike["state"] = constants.NORMAL
if self._sovelluslogiikka.tulos == 0:
self._nollaus_painike["state"] = constants.DISABLED
else:
self._nollaus_painike["state"] = constants.NORMAL
self._syote_kentta.delete(0, constants.END)
self._tulos_var.set(self._sovelluslogiikka.tulos)
Komennoilla on nyt siis metodi suorita
ja ne saavat konstruktorin kautta Sovelluslogiikka
-olion ja funktion, jota kutsumalla syötteen voi lukea.
5. Komentojen kumoaminen
Toteuta laskimeen myös kumoa-toiminnallisuus. Periaatteena on siis toteuttaa jokaiseen komento-olioon metodi kumoa
. Olion tulee myös muistaa mikä oli tuloksen arvo ennen komennon suoritusta, jotta se osaa palauttaa laskimen suoritusta edeltävään tilaan.
Jos kumoa-nappia painetaan, suoritetaan sitten edelliseksi suoritetun komento-olion metodi kumoa
.
Riittää, että ohjelma muistaa edellisen tuloksen, eli kumoa-toimintoa ei tarvitse osata suorittaa kahta tai useampaa kertaa peräkkäin. Tosin tämänkään toiminnallisuuden toteutus ei olisi kovin hankalaa, jos edelliset tulokset tallennettaisiin esimerkiksi listaan.
6. Retrospektiivitekniikat
Wikipedian mukaan retrospektiivi on “a meeting held by a project team at the end of a project or process (often after an iteration) to discuss what was successful about the project or time period covered by that retrospective, what could be improved, and how to incorporate the successes and improvements in future iterations or projects.”
Tutustu täällä esiteltyihin retrospektiivitekniikoihin Start, Stop, Continue, More of, Less of Wheel ja Glad, Sad, Mad.
Tee aiheesta noin 0.25 sivun (eli noin 125 sanaa) tiivistelmä repositorion juureen sijoitettavaan tiedostoon retro.md.
Pidä huoli siitä, että miniprojektitiimisi pitää ensimmäisen sprintin lopussa jompaakumpaa tekniikkaa noudattavan retrospektiivin!
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