/
prosessi.tex
193 lines (152 loc) · 10.2 KB
/
prosessi.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
\section{Ongelmanratkaisuprosessi}
Tässä osiossa esittellään ensin yleinen ongelmanratkaisuprosessi, minkä jälkeen
sitä sovelletaan ohjelmointiin.
\subsection{Yleinen ongelmanratkaisuprosessi}
Yleistä ongelmanratkaisuprosessia on tutkittu pitkään ja se on sovellettavissa
monelle alalle, myös ohjelmointiin. Descartes, Hyman ja Anderson ovat
muodostaneet yleiselle ongelmanratkaisuprosessille rungon, jossa on
seuraavanlaisia piirteitä \cite{Gries:1974:WTI:953057.810447}.
Mitään asiaa ei tule koskaan hyväksyä totena ellei sille ole varmaa ja
perusteltua tietoa. Jos asian paikkaansapitävyyttä ei tiedetä, sen ei pidä
olettaa olevan totta. Tästä on apua varsinkin silloin, kun etsitään tietoa
ongelmaan ja sen ratkaisutavoista. Lähteiden taustat tulee tarkistaa huolella
ennen kuin aletaan soveltaa saatua tietoa. Korjaaminen voi olla haastavaa, jos
on sovellettu virheellistä tietoa.
Vaikeat asiat tulee jakaa niin moneen osaan kuin mahdollista. Pieniä asioita on
helpompi hallita kuin isoja. Tämän seurauksena yhden ison ongelman sijaan on
monta pientä ongelmaa, jotka voi olla hyvinkin helppoja ratkaista. Voi olla
hyvinkin mahdollista, että pienemmät ongelmat ovat entuudestaan tuttuja, joten
niihin löytyy nopeasti ratkaisu.
Toiset ongelmat ovat haastavampia kuin toiset. Kannattaa ensin ratkaista helpot
ongelmat ja sitten vaikeammat. Helpot ongelmat voivat olla nopeita ratkaista ja
niiden ratkaiseminen tuo itsevarmuutta vaikeampien ongelmien parissa.
Johtopäätöksessä tulee ilmaista kaikki ongelmaan liittyvät seikat sekä sen tulee
olla niin yleispätevä, ettei sitä voi sivuuttaa. Kaikki ongelmaan liittyvät
asiat tulee esitellä ja ne tulee ilmaista siten, ettei niitä voi tulkita väärin.
Aluksi on tärkeä käydä kaikki ongelman yksityiskohdat nopeasti moneen kertaan
lävitse, jotta kokonaiskuva ongelmasta selviää. Esimerkiksi ruuanvalmistuksessa
on syytä käydä kaikki vaiheet lävitse, jotta tiedetään, mitä aineksia ja
välineitä tarvitsee ostaa tai ottaa esille. Tämän jälkeen ongelma on
yksinkertaista jakaa moneen osaan. Ruokareseptit on jaettu pienempiin
yksinkertaisiin vaiheisiin.
Esimerkiksi tutkielman kirjoittamiseen voidaan soveltaa yleistä
ongelmanratkaisuprosessia. Ensin on saatava yleiskuva aiheesta, joka voisi olla
esimerkiksi, tietokonepelien vaikutus opiskeluun. Kun on yleiskuva, niin siitä
voi kaivaa esiin pääpiirteet, joita ovat esimerkiksi millaisia
tietokonepelityyppejä on. Pääpiirteet voidaan jakaa pienempiin alilukuihin,
kuten väkivaltaa sisältävät pelit ja opetuspelit. Lopuksi tehdään yhteenveto,
jossa kerrataan ongelmakohdat ja tutkimustulokset. Tässä on ensin jaettu isommat
kokonaisuudet pienempiin ja vielä pienempiin osiin, jotka on sitten kirjoitettu
auki.
\subsection{Ohjelmointiin soveltaminen}
Yleinen ongelmanratkaisuprosessi on sovellettavissa ohjelmointiin ja sen
seuraaminen tuo ohjelmointiin määrätietoisuutta ja selkeyttä.
\subsubsection{Griesin analogia}
Gries esittelee hyvän analogian ohjelmointiin liittyvästä
ongelmanratkaisuprosessista \cite{Gries:1974:WTI:953057.810447}:
\begin{quotation}
``Kaapinvalmistuskurssilla ohjaaja esittelee sahan, työtason, vasaran ja
muutaman muun työkalun muutamassa minuutissa. Tämän jälkeen hän näyttää
kauniin valmiin kaapin ja antaa opiskelijoille pari viikkoa aikaa tehdä oma
kaappi. Ajattelisit ohjaajan olevan hullu!''
\end{quotation}
Analogia kuvaa hyvin ohjelmoinnin opetusta. Opiskelijoille annetaan työkalut ja
näytetään pari esimerkkiohjelmaa, mutta ei kerrota, miten esimerkkiohjelmat on
toteutettu ja miksi. Tämän tyyppinen opetus rikkoo yleisen
ongelmanratkaisuprosessin pääpiirteitä.
Ongelmalähtöisessä opetuksessa opiskelijoille tulee kuvata ensin ongelma ja
sitten näyttää miten sitä lähdetään pilkkomaan pienempiin ongelmapalasiin. Tämän
jälkeen tulee kertoa, mitä ohjelmointirakenteita käytetään minkäkin osaongelman
kohdalla. Lopulta opiskelijalle on selvää, mistä ohjelma koostuu ja miksi
mikäkin rakenne on kuhunkin kohtaan valittu.
\subsubsection{Mañana-periaate}
Tämä luku käsittelee Mañana-periaatetta, joka pohjautuu lähteeseen
\cite{Caspersen:2006:NPO:1176617.1176741}.
Yksi tapa soveltaa yleistä ongelmanratkaisuprosessia on jakaa vaikeammat asiat
aliohjelmakutsujen taakse ja toteuttaa varsinaiset aliohjelmat myöhemmin. Tähän
ideaan pohjautuu Mañana-periaate. Vaikea asia voi olla sellainen, johon
ohjelmoija ei keksi heti ratkaisua.
Vaikeat asiat eivät ole ainoa tilanne, jolloin tulee käyttää Mañanaa. Jos
ohjelmassa tulee vastaan jokin erikoistapaus, sille tulee tehdä oma aliohjelma.
Tälläinen erikoistapaus on esimerkiksi janan piirtämisessä tilanne, jossa
kummatkin päätepisteet ovat samassa koordinaatissa.
Ohjelmakoodia tulee aina välillä refaktoroida. Tällöin Mañanan mukaan
monimutkaiset koodinpätkät tulee sijoittaa omiin aliohjelmiin. Pitkät lauseet,
kuten usean vaiheen sisältävät ehtolauseet, tulee refaktoroida omiin
aliohjelmiin, jolloin koodi säilyy lyhyenä ja helpommin luettavana.
Jos jokin koodipätkä toistuu useaan kertaan, se tulee eriyttää aliohjelmaan.
Tällöin, jos kyseiseen pätkään tulee muutos, riittää, että ohjelmoija tekee
tarvittavan muutoksen vain yhteen paikkaan ja koko ohjelma toimii halutulla
tavalla. Sisäkkäisten silmukoiden kohdalla sisäsilmukan koodi tulee sijoittaa
omaan aliohjelmaan.
Mañana-periaatetta noudattaessa koodi säilyy helposti luettavana ja ohjelman
oikeellisuus on helppo todentaa. Suuremmat kokonaisuudet koostuvat pienistä
osista, joita on helppo tarkastella ja niiden toimivuus todentaa. Täten
suuremmat kokonaisuudet pystytään osoittamaan toimiviksi.
\subsubsection{Järjestelmällinen ohjelman kasaaminen}
\label{järjestelmällisyyteen pohjautuva ongelmanratkaisuprosessi}
Järjestelmällisen ohjelman kasaamisen näyttäminen opiskelijoille ohjaa
systemaattiseen ajatteluun ja noudattaa yleistä ongelmanratkaisuprosessia.
Tanskassa on tutkittu tätä varsin hyvin tuloksin
\cite{Caspersen:2006:NPO:1176617.1176741}.
Opiskelijoille annetaan rajapinta, joka ratkaisun pitää toteuttaa. Rajapinnan
pohjalta he luovat luokan ja metodirungot. Metodirunkojen tulee olla
mahdollisimman yksinkertaisia eli niiden pitää sisältää tarvittavat tiedot,
jotta koodi menee kääntäjästä lävitse. Jos metodi ei palauta mitään, se on
tyhjä, ja muussa tapauksessa se palauttaa tyhjää \texttt{null} tai \texttt{0}
tietotyypistä riippuen.
Tyhjän luokkarakenteen jälkeen luodaan testit. Testit ovat joko yksikkötestejä
tai aloittelijoiden tapauksessa pääohjelmaan kirjotettuja kutsuja, jotka
määrittelevät luokan toiminnan. Testien on mentävä kääntäjästä lävitse vaikkei
niiden ajaminen tuota oikeaa tulosta.
Testien jälkeen voidaan miettiä vaihtoehtoisia esitystapoja ilmentymämuuttujien
avulla. On tärkeää painottaa, että toteutusmalleja on useita erilaisia, joten
tässä vaiheessa on löydettävä ainakin kaksi erilaista vaihtoehtoa. Esimerkiksi
päivämäärän voi tallentaa päivinä, kuukausina ja vuosina tai päivinä ajanlaskun
alusta. Kun vaihtoehdot ovat selvillä, arvioidaan metodien toteutuksien
haastavuutta eri vaihtoehdoille. Pelkkien kuluneiden päivien antaminen on
todella yksinkertaista toteuttaa, kun taas päivissä, kuukausissa ja vuosissa
ilmoitettuna tarvitaan erilaisia tarkistuksia, jotta tieto on oikein.
Päivämäärän tulostaminen on taas helppoa, kun eri osat on tallennettu erikseen.
Eri esitysvaihtoehdoista valitaan keskimäärin helpoin toteuttaa. Luodaan
ilmentymämuuttujat alustuksineen ja määritellään raja-arvot, kuten kuukausissa
mahdolliset arvot ovat yhden ja kahdentoista välillä. Tämän jälkeen toteutetaan
metodit. Metodien toteuttamiseen Caspersen on selvittänyt hyvän algoritmin:
Metodien toteuttamisella ei ole järjestystä, mutta on suositeltavaa toteuttaa
ensin helpoin ja siirtyä sitten seuraavaksi helpompaan. Metodi on valmis, kun
siinä ei ole mitään refaktoroitavaa. On tärkeää pitää vain yksi metodi
kerrallaan työn alla, sillä siten mahdolliset ongelmat rajautuvat kyseiseen
metodiin.
\subsection{Hyvä koodi}
Hyvällä koodilla (clean code) on useita määritelmiä, mutta pääpiirteet ovat
samoja \cite{Martin:2008:CCH:1388398}. Ohjelmointiin liittyvien
ongelmanratkaisuprosessien tuloksena syntyvä koodi on yleensä hyvää koodia.
Hyvän koodin kirjoittaminen on tärkeä asia ohjelmoinnissa, sillä huono koodi
hidastaa kehitystä ja mahdollistaa virheiden syntymisen. Yleinen virhe on
todeta, että korjaa huonon koodin myöhemmin, sillä yleensä myöhemmin tarkoittaa
ei koskaan. Tällöin huono koodi jää elämään.
Koodin luettavuus on tärkeä. Logiikan tulee olla suoraviivaista, jolloin virheet
eivät huku epäselvän logiikan sekaan. Hyvää koodia on mieluisa lukea ja siitä
saa hyvän kuvan ohjelman toiminnallisuudesta. Myös muidenkin kuin alkuperäisen
kehittäjän tulee pystyä ymmärtämään koodi.
Nimeämiskäytännön tulee olla selkeä ja asioilla tulee olla selkeät nimet.
Koodissa ei tule olla toistoa. Eri asioiden, kuten luokkien, metodien,
funktioiden ja samankaltasuuden, määrä tulee minimoida.
Riippuvuuksien tulee olla vähäisiä, mikä helpottaa ylläpitämistä. Riippuvuuksien
pitää olla selkeästi määriteltyjä ja niiden pitää tarjota selkeä, pieni
käyttörajapinta.
Yksikkötestit on tärkeä osa hyvää koodia, sillä ne kertovat koodin
toimivuudesta. Testattua koodia on helppo parantaa ja testit tuovat varmuutta,
että muutettu koodi tekee myös ne asiat, joita se alunperin teki.
Virheidenkäsittelyn tulee pysyä samana sovelluksen eri osissa. Testit
määrittävät koodin ulkoisen rajapinnan, joten niillä on helppo valvoa
virheidenkäsittelyä.
Testivetoinen kehitys (lyhyesti TDD) on yleinen tapa kirjoittaa koodia. TDD:ssä
ensin kirjoitetaan testi, jonka jälkeen testin toteuttava koodi. Tällä tavoin
meneteltynä koodista tulee hyvää ja testattua.
Suorituskyvyn tulee olla hyvä, mutta se ei saa tuoda mukanaan sekavuutta
koodiin. Jotkut optimoinnit voivat olla todella rumia, joten ne on syytä jättää
pois.
Hyvä koodi on yleisesti ottaen myös kaunista koodia, joka on kuin ongelmaa
kuvaava kieli. Jokainen koodin osa tekee juuri sen asian, mikä sen on
tarkoituskin tehdä eikä yhtään enempää.