Opintojen meditatiivinen osuus

Viimeksi saatiin piirrettyä iPhonen ruutuun, kosketusnäytön tarkkailusta nähtiin jonkinlainen esimerkki jo aiemmin ja samalla päästiin jonkinlaiselle hajulle, miten ajastintapahtumilla (timer event) voi korvata perinteisen main loop -ajattelumallin jatkuvasti toistuvien tilannepäivitysten hoitelussa. Toisin sanoen yksinkertaisen peliapplikaation perusainekset ovat jotakuinkin kasassa.

Jottei kuitenkaan kiivettäisi perse edellä kaakaopensaaseen, pitäisi päästä edes hieman jyvälle iPhone-softan arkkitehtuurista. Järkikin sanoo (Quartz-oppaan kompatessa), ettei softaa voi vaan ruveta kasaamaan drawRect: metodiin – sen tehtävä on päivittää näytön sisältö mahdollisimman nopeasti, eikä nysväillä tilannepäivitysten tai muun yleisen säädön parissa. Olisiko itse asiassa optimaalisinta (tai edes mahdollista) naputella jopa nuo piirtokomennot graphic contextiin ihan jossain muualla ja drawRect: -metodissa vain sitten pyöräyttää läpi valmiiksi laadittu piirtosarja? En tosiaan tiedä, joku kokeneempi voisi vihjata tässä oikeaan suuntaan. No joka tapauksessa, jonkinlainen runko ja jäsentely softalle tarvitaan, vaikka se olisikin vain pikkuruinen viihdeapplikaatio – muuten virtuaaliroskis alkaa ennen pitkää rapsahtelemaan lukukelvottomiksi käyneistä lähdekoodinpätkistä.

Olio-ohjelmoinnista on kirjoitettu tuhat kirjaa ja miljoona nettiopasta, joihin ei minun asiantuntemuksellani ole mitään lisättävää, joten en ala sitä tässä kovin syvällisesti ruotimaan. Koska se kuitenkin on yksi Cocoa-kehityksen kulmakivi, hahmoteltakoon tässä sen periaatteet kuten olen ne ymmärtänyt.

Olio-ohjelmointi ottaa tietyssä mielessä mallia siitä, miten asiat toimivat todellisessa maailmassa. Esimerkiksi autolla on tiettyjä tehtäviä (eteneminen, peruuttaminen), ominaisuuksia (väri, moottorin teho) sekä käyttöliittymä (kaasupoljin, ratti), jotka yhdessä tekevät siitä auton. Esimerkiksi käsite ”Auto” olisi olio-ohjelmoinnissa luokka eli class, ja yksittäinen autoyksilö, vaikka naapurin savuttava Samara olisi luokan ”Auto” instanssi. Siis:

Auto *naapurinSamara;

Auton kuljettaja taas olisi:

Ihminen *naapurinTaisto;

Jotta nämä oliot voisivat tehdä yhdessä jotain hyödylllistä, vaikka kuljettaa Taisto kansalaisopiston huovutuskurssille, ei Taiston tarvitse eikä kuulu tietää autonsa jokaikisen osan toimintaa läpikotaisin (saatika että auton tarvitsisi tietää Taiston aineenvaihdunnan yksityiskohtia). Riittää kun Taisto tietää metodit, joilla autoa ohjataan. Samanniminen ominaisuus kahdella oliolla ei myöskään aiheuta ongelmia, kuten saattaisi käydä proseduraalisessa ohjelmoinnissa globaaleja muuttujia käytettäessä – Taiston ei siis tarvitse pelätä, että hänen painaessaan kaasupoljinta, jolloin auton ominaisuus ”kaasu” vaikka tuplaantuisi, Taiston suoliston epäonnisesti nimetty ominaisuus ”kaasu” kasvaisi samaan tahtiin aiheuttaen vaivautuneisuutta huovutuskurssilla.

Olio on siis hyvin itsenäinen otus, jonka täysipainoinen hyödyntäminen onnistuu vaikket tietäisi siitä mitään muuta kuin sen käyttöliittymässä (@interface) määritellyt julkiset metodit. Tämähän on kieltämättä erittäin kätevää, kun ohjelmointiympäristö on laajuudeltaan nykyaikaisen käyttöjärjestelmän luokkaa. Se tarkoittaa kuitenkin myös sitä, että uuden olion käyttöliittymä täytyy suunnitella hyvin ja toteuttaa erittäin pedantisti, jotta homma toimisi.

Koska olion sisäisiin ominaisuuksiin ei tosiaan muilla ole mitään asiaa, ja toisaalta niitä useimmiten kuitenkin pitää päästä muokkaamaan jotta mitään tapahtuisi, tarvitaan jokaista julkista ominaisuutta varten ”getter”- ja ”setter”-metodit. Aika puuduttavaa naputtelua, eikös? Siinä tuleekin apuun tuo aiemmin ihmettelemäni @property -> @synthesize -parivaljakko. Se luo nämä getter- ja setter-metodit automaattisesti, ja nimeää ne vakiokäytännön mukaisesti, jonka jälkeen niitä voi käyttää aivan normaalisti kuten olisit nakutellut ne koodiin itse. Myös niiden suluissa olevien lisämääritysten merkitykset selvisivät täältä (paitsi se epäatominen, jonka hatarasti ymmärsin liittyvän jotenkin moniajoon ja päällekkäisiin prosesseihin, ”threads”).

No, uuden object classin luominen (vaikkapa sen ”Auto”-classin jotain kaahailupeliä varten) ei ole mitenkään monimutkaista hommaa, kunhan seurailee oppaiden määrittelemää syntaksia. Hämärä alue alkaa omalla kohdallani edelleen siitä, missä vaikkapa nyt tuon Auto-luokan instanssit olisi järkevä luoda, ja esimerkiksi missä tarkkaan ottaen määriteltäisiin se timer action -toiminto, jolla pelin autojen sijainnit ym. ominaisuudet päivitettäisiin.

Yhdenlaisen, joskin vielä melkoisen abstraktin vastauksen tarjoaa oppaissa usein toistuva termi ”MVC” eli ”Model – View – Controller design pattern”. Äärimmilleen yksinkertaistettuna kyse on siitä, että ”Model” – ohjelman perusdata ja sen käsittelyyn tarvittavat metodit (johon tuo Auto-luokkakin kuuluisi), ”View” – eli autojen ja maiseman piirto näytölle ja käyttäjän toimien tarkkailu ja ”Controller”, eli kahden edellämainitun välillä toimiva ”komentokeskus” pidetään visusti erillään. Tämä on selvästi eräänlainen jatke olio-logiikan ”itsenäisyysperiaatteesta”, ja helpottanee erityisesti monimutkaisten ohjelmistojen kurinpitoa ja päivittämistä. Pienten ohjelmien laatimiseen periaate tuntuu kuitenkin edelleen lisäävän ylimääräistä ”byrokratiaa”. Opettelemassa kun tässä kuitenkin ollaan, koitin motivoida itseäni keksimällä periaatteesta jotain käytännön etuja, ja kyllähän niitä hetken mietinnän jälkeen löytyikin.

Ensinnäkin, jos pitäisin varsinaisen näkymän piirron erillään pelin perustoiminnallisuuksista, eli en esimerkiksi kutsuisi näytön piirtotoimintoja suoraan tuolta Auto-luokasta, vaan tämä ”Controller” välittäisi Auto-instanssien piirtämiseen tarvittavat tiedot näkymälle, niin – silloinhan voisin kokeilla näkymässä mitä vain rendaustyylejä koskematta Auto-luokkaan lainkaan. Itse asiassa pystyisin varmaan laatimaan monta rinnakkaista visutyyliä, joita voisi vaihtaa vaikka kesken pelin preferenssejä säätämällä, jolloin vaihdettaisiin vain kutsuttava näkymäinstanssi toiseen. Ihan näppärää näin pienkehittäjänkin näkökulmasta!

Viimeksi piirrettiin ruutuun luomalla UIView:lle oma alaluokka TreeniView ja määrittelemällä sen drawRect -metodi uudestaan. Olisikohan tuon UIViewController -luokan instanssi sitten hyvä paikka alkaa kasaamaan pelin ”komentokeskusta”, jossa pelidatan päivitysmetodeja ja näytön piirto- ja tarkkailumetodeja kutsuttaisiin? Pitääpä perehtyä tarkemmin… siihen asti, öitä!

Vastaa

Täytä tietosi alle tai klikkaa kuvaketta kirjautuaksesi sisään:

WordPress.com-logo

Olet kommentoimassa WordPress.com -tilin nimissä. Log Out / Muuta )

Twitter-kuva

Olet kommentoimassa Twitter -tilin nimissä. Log Out / Muuta )

Facebook-kuva

Olet kommentoimassa Facebook -tilin nimissä. Log Out / Muuta )

Google+ photo

Olet kommentoimassa Google+ -tilin nimissä. Log Out / Muuta )

Muodostetaan yhteyttä palveluun %s

%d bloggers like this: