SEPA-päiväkirja Aihe: Refaktorointi Tekijät: Markku Huttunen, Antti Poikela 1 Sisällysluettelo 1. Sisällysluettelo 2. Johdanto 3. Menetelmän käyttö 4. Kokemukset ja muutokset 5. Lähdeluettelo 2 Johdanto SEPA-aiheeksemme valitsimme refaktoroinnin. Ohjelmistokehityksessä tämä tarkoittaa lyhyesti sitä, että ei ole erillistä suunnitteluvaihetta ennen buildien tekoa, vaan suunnittelua muokataan tuotteen kehityksen aikana muuttamatta sen ulkoista toimintaa. [1] Refaktorointia käytetään ohjelmistokehityksessä jatkuvasti, osin sitä edes tiedostamatta, ohjelmiston ymmärrettävyyden ja toiminnan loogisuuden parantamiseksi. Refaktorointia voidaan suorittaa myös erillisenä työtehtävänä, jonka tavoitteena on parantaa koodin laatua esimerkiksi paremman ylläpidettävyyden ja laajennettavuuden saavuttamiseksi. Tämä voi olla tähdellistä varsinkin, jos joudutaan työskentelemään jo olemassa olevan koodin kanssa, jonka laadussa olisi parannettavaa, mutta jota ei kuitenkaan keretä kirjoittamaan uudelleen. Käytännössä refaktorointi näkyy ohjelman lähdekoodin uudelleenjärjestämisenä, usein erilaisten automatisoitujen työkalujen avulla. Valitimme refaktoroinnin aiheeksemme, koska siitä on todennäköiseesti hyötyä kaikille kehittäjille projektissamme. Lisäksi käyttämämme kehitystyökalu, Eclipse, tukee refaktorointia erinomaisesti. Uskomme myös, että refaktoroinnin avulla pystymme paremmin noudattamaan konventioita ja näin ollen tuottamaan yhdenmukaisempaa ja ymmärrettävämpää koodia. 3 Menetelmän käyttö Refaktorointia tulevat käyttämään kaikki kehittäjät. Tarkoituksena on käyttää refaktorointia jatkuvasti kehityksen aikana. Hyvä käytäntö on tarkistaa refaktorointitarve jokaisen metodin kirjoittamisen jälkeen sekä jokaisen työskentelyjakson päätteeksi. Vähintäänkin koodin ulkoasun muotoilu konventioiden mukaiseksi työrupeaman päätteeksi on välttämätöntä, jotta muun muassa testaajan työaika ei kuluisi hukkaan. Kannattavaa on myös varmistaa metodin järkevä suunnittelu metodin kirjoittamisen jälkeen, sillä myöhemmin tehtynä metodin toiminnan joutuu turhaan kertaamaan uudelleen. Myöhemmässä vaiheessa tapahtuvan refaktoroinnin jälkeen on aina syytä ajaa uudelleen mahdolliset automatisoidut testit koodin toiminnan varmistamiseksi.
Alla käymme läpi Eclipsen keskeisimpiä refaktorointitoimintoja, joita kehittäjien on suositeltavaa käyttää projektin kuluessa tarpeen vaatiessa. Suurimman osan operaatioista voi suorittaa myös manuaalisesti, mutta Eclipsen avulla se on nopeampaa ja turvallisempaa muun muassa viittauksien eheys säilyy. Kehittäjät käyttävät refaktorointitoimenpiteitä itsenäisesti oman harkintakykynsä mukaan. Eclipse tarjoaa muun muassa loistavat uudelleennimeämis- ja siirtomahdollisuudet kaikilla koodin tasoilla (mm. pakettien, metodien ja luokkien) säilyttäen koodin sisäiset viittaukset ehjinä. Siirtäminen toimii sekä Eclipsen Refactor -valikosta (Move) että Package Explorer -näkymässä drag&drop -toiminnolla. Siirtotoimintoja käytetään designin korjaamiseen, esimerkiksi kytkentöjen ja koheesion parantamiseen. Tarpeen sille voi havaita koodia tutkimalla tai esimerkiksi Together -työkalun metriikka-analysaattorilla. Change Method Signature -toiminnolla voidaan muuttaa metodin parametreja, näkyvyyksiä, palautusarvoja ja poikkeuksia siten, että viittaukset muualla koodissa pysyvät ehjinä. Convert Anonymous Class to Nested muuttaa nimensä mukaisesti anonyymin luokan sisäkkäiseksi (vrt. inner eli sisäinen) luokaksi. Tästä voi olla etua mm. haluttaessa välttää koodin kopiointia. Anonyymia luokkaa tulisi käyttää lähinnä vain ns. singleton -tapauksissa, ts. kun sisäisestä luokasta ei haluta tehdä kuin yksi instanssi (näin on usein muun muassa graafisten sovellusten kuuntelijoiden tapauksissa). Toiminnolla Move Member Type to New File voidaan erottaa sisäisiä luokkia uusiksi ulkoisiksi tiedostoiksi. Toiminto on hyödyllinen, jos sisäinen luokka esimerkiksi kasvaa kovin pitkäksi. Pull Up ja Push Down siirtävät metodeita ja kenttiä ylempiin ja alempiin luokkiin luokkahierarkiassa. Voidaan käyttää, jos esimerkiksi alaluokan kenttä tai metodi ei ole niin spesifinen, etteikö se olisi helposti yleistettävissä yläluokkaansa. Esimerkkinä voisi toimia Lehmä -luokalle tehty oliontuhoamismetodi kuole() joka paremminkin voisi esiintyä yläluokassa Eläin. Tällöin käytämme Pull Up -toimintoa. Extract Interface -toiminnolla voidaan luokan julkisista metodeista erottaa rajapinta. Toiminto osaa myös ottaa tämän uuden rajapinnan käyttöön sen toteuttavien luokkien sijasta muiden luokkien instanssimuuttujissa, mikä onkin hyvien koodauskäytäntöjen mukaista. Generalize Type muuttaa viittauksen sen mahdolliseksi yleistykseksi. Tämä toiminto voidaan suorittaa tyyppiviittauksille, kenttien ja paikallisten muuttujien määrityksille ja referenssityypitetyille parametreille. Toiminto tarkistaa, voidaanko muunnos suorittaa turvallisesti. Use Supertype Where Possible -toiminto voidaan ajaa luokalle, jolloin etsitään kyseisen projektin polulla olevista paketeista viittauksia kyseiseen luokkaan ja muutetaan ne halutuksi yleistykseksi, jos mahdollista. Toimii siis kuten Generalize Type, mutta ainoastaan tyypeille ja suoritetaan kerralla koko luokalle. Infer Generic Type Arguments -toiminnolla voidaan muuttaa raakojen kokoelmien (Collection) esiintymät niiden tyypitetyiksi versioiksi (esim. Collection<String>), jos mahdollista. Voidaan suorittaa joko yksittäiselle luokalle, paketille tai jopa koko projektille kerralla. Tyypitettyjen kokoelmien käyttö on erittäin suositeltavaa (muun muassa vältytään tyyppimuunnoksilta) ja siksi suosittelemme tämän toiminnon käyttämistä.
Extract Method luo uuden metodin valitusta koodipätkästä. Tämän toiminnon voi varsin helposti tehdä myös käsin ilman Eclipsen apua, ja haluammekin korostaa itse käytäntöä pitkien metodien sijaan kannattaa usein erottaa loogisia kokonaisuuksia yksityisiksi metodeiksi koodin hahmotettavuuden parantamiseksi. Sama tilanne on vakioiden ja paikallisten muuttujien tapauksissa, joiden erottamiseen Eclipse myös tarjoaa vastaavan työkalun. Kaikissa näissä refaktorointitoiminnoissa on esikatseluominaisuus, jonka avulla tulevia muutoksia voi tarkastella ennen niiden suorittamista. 4 Kokemukset ja muutokset Kappaleessa käymme läpi kehittäjien kokemuksia refaktoroinnista iteraatioiden aikana. Lisäksi listaamme mahdolliset muutokset käytäntöön. 1 Suunnitteluvaihe Suunnitteluvaiheessa ei vielä saatu varsinaisia kokemuksia refaktoroinnin käytöstä, koska ohjelmointitehtävät alkoivat vasta seuraavassa iteraatiossa. 2 Iteraatio 1 Keräsimme kokemuksia refaktoroinnin käytöstä ensimmäisen iteraation aikana lähettämällä kehittäjille sähköpostikyselyn. Kysyimme, kuinka paljon ja minkä tyyppistä refaktorointia kehittäjät käyttivät. Kysyimme myös heidän mielipiteitään refaktoroinnin käytön tarpeellisuudesta ja sen vaikutuksista ohjelmiston laatuun. Kyselystä kävi ilmi, että jokainen kehittäjä käytti refaktorointia projektissamme. Keskimäärin kehittäjät ovat käyttäneet ohjelmointiajastaan alle 10% refaktorointiin. Lähinnä Valppaan kehittäjät käyttivät hieman muita enemmän aikaa refaktorointiin. Useimmiten refaktorointia käytettiin koodin luettavuuden ja ymmärrettävyyden parantamiseen. Suosituimmat refaktorointimenetelmät olivat koodin automaattinen formatointi sekä uudelleennimeämistoiminnot. Osa kehittäjistä käytti refaktorointia myös koodin rakenteen muokkaamisessa esimerkiksi luokkien vastuiden muutoksissa ja toimintojen uudelleenjärjestämisessä. Eclipsen tarjoamasta runsaasta refaktorointivalikoimasta käytettiin kuitenkin vain murto-osaa. Kukaan kehittäjistä ei kokenut refaktoroinnin käyttöä työläänä vaan se nähtiin ennemminkin erottamattomana osatekijänä ohjelmointitehtävissä. Kehittäjät eivät oppineet ensimmäisen iteraation aikana refaktoroinnista varsinaisesti mitään uutta, vaan käytetyt menetelmät olivat heille tuttuja entuudestaan. Itse opimme uusista refaktorointimenetelmistä jo edellisessä iteraatiossa jolloin teimme SEPA:n ensimmäisen vaiheen. Käyttämämme IDE:n tarjoamat automaattiset refaktorointiominaisuudet koettiin varsin hyviksi, lukuun ottamatta koodin automaattisen formatoinnin toisinaan vajavaista toimintaa. Varsinkin Eclipsen tarjoama esikatselutoiminta nähtiin hyvänä ominaisuutena. Kokonaisuutena koettiin, että Eclipse toimi hyvänä apuna refaktoroinnissa ja kannusti sen käyttöön.
Ryhmämme kokee, että refaktoroinnin käyttö paransi ohjelmistomme laatua. Esimerkiksi muuttujannimien vaihto käyttämällä Find/Replace -toimintoa ei olisi todennäköisesti johtanut yhtä hyvään lopputulokseen. Aika-ajoin koettiin kuitenkin, että Eclipsen formatointitoiminto teki koodista epäselvempää huonoilla rivitysvalinnoilla. 3 Iteraatio 2 Iteraation alussa järjestimme kehittäjille noin puolen tunnin koulutustilaisuuden, jossa kävimme yksityiskohtaisesti läpi myös tässä dokumentissa esitellyt Eclipsen tärkeimmät refaktorointitoiminnot. Lisäksi kehittäjille oli varattu iteraation alusta puolentoista tunnin aika oman koodin läpikäymiseen refaktorointitarpeen varalta. Koska ensimmäisen iteraation lopussa lähettämämme sähköpostikysely kehittäjien refaktoroinnin käytöstä osoittautui näppäräksi keinoksi saada yleiskuva refaktoroinnin määrästä ja menetelmistä, päätimme tehdä samantyyppisen kyselyn myös Iteraatio 2:n lopussa. Tässä kyselyssä tiedustelimme kehittäjiltä perinteisesti, kuinka paljon he käyttivät refaktorointia ohjelmointityössään. Tämän lisäksi kysyimme, kokivatko he hyödylliseksi vuoden alussa pitämämme refaktorointikoulutuksen. Kuten ensimmäisessäkin iteraatiossa, myös toisessa kaikki kehittäjät tekivät refaktorointia. Keskimäärin refaktorointiin käytettiin kehittäjien omien arvioiden perusteilla noin 15% ohjelmointiajasta. Tosin vaihtelevuutta löytyi huimasti kehittäjien välillä joku tunnusti käyttäneensä ajasta alle 5% refaktorointiin kun taas toinen kertoi käyttäneensä jopa 50%. Kaikki kehittäjät myönsivät, että Eclipsen työkaluesittelystä oli hyötyä ainakin tulevaisuuden kannalta, joskaan kaikkien mielestä ei suoranaisesti tämän projektin kannalta. Eclipsen työkaluista monet tekevät aika isoja muutoksia, jonka takia kaikkia ei tarvittu käyttää projektissa. Kaksi viidestä kehittäjästä sanoo oppineensa jotain uutta refaktoroinnista tämän iteraation aikana. Kysyimme lisäksi oliko kehittäjien mielestä Eclipsen tarjoamissa työkaluissa puutteita. Yksi oli sitä mieltä, että oli välillä epävarma olo, toimivatko ne oikein, ja toisen mielestä joissain tilanteissa oli epäselvää, mikä kohta koodista täytyisi olla valittuna tiettyä toimintoa tehdessä. Muilta osin kehittäjät kokivat, ettei työkaluissa ollut puutteita. Vaikka Eclipsen tarjoama työkaluvalikoima onkin laaja, monet kehittäjät tekivät varsinkin monimutkaisimmat refaktorointitehtävät käsin. Automaattisia toimintoja käytettiin lähinnä uudelleennimeämisiin ja metodien ja muuttujien irrottamiseen koodista. Suurin osa kehittäjistä oli sitä mieltä, että refaktoroinnin käyttö paransi ohjelmistotuotteemme laatua merkittävästi. 4 Yhteenveto Yhteenvetona voidaan todeta aiheen olleen hyödyllinen projektin kannalta, sillä kaikki kehittäjät olivat sitä mieltä, että ohjelmiston laatu on merkittävästi parantunut refaktoroinnin ansiosta. Lisäksi järjestetty pikakoulutus ja lyhyt erillinen refaktorointihetki pakotti kehittäjät miettimään refaktorointitarvetta, ja vastaukset kertoivatkin kaikkien oppineen refaktoroinnista uutta. Itsellemme aiheesta voisi todeta syvyyden hieman loppuneen ensimmäisen iteraation jälkeen. Merkittävimmät uudet asiat ilmenivät ensimmäisessä iteraatiossa, ja lisäksi Antilla ei ollut lainkaan koodaustehtäviä toisessa iteraatiossa, joten refaktorointia hän ei päässyt
soveltamaan käytännössä. Ongelmallista refaktoroinnin yhteydessä oli sen käyttömäärän arviointi, ts. kuinka suuri osa kehittäjien ajasta kului refaktoroinnin parissa. Koska arviot vaihtelivat jopa 5 ja 50 prosentin välillä, on hyvinkin mahdollista, että kehittäjille jäi erilaiset kuvat siitä, mitkä toimet ovat refaktorointia. Toisaalta ero voi myös ainakin osittain selittyä myös toimintatapojen erilaisuudella. Todellisia käyttölukuja tärkeämpää on kuitenkin se, että refaktorointia selvästi käytettiin läpi projektin ja se koettiin yksinomaan hyödylliseksi. 5 Lähdeluettelo [1] Schach, Stephen R. Object-Oriented & Classical Software Engineering, 6th ed., 2002. [2] Eclipsen manuaali. 2005.