Formaalit menetelmät ja testaus: Abstraktien tietotyyppien käyttö yksikkötestauksessa

Samankaltaiset tiedostot
Kehittää ohjelmointitehtävien ratkaisemisessa tarvittavia metakognitioita!

A TIETORAKENTEET JA ALGORITMIT

FORMAALI SYSTEEMI (in Nutshell): aakkosto: alkeismerkkien joukko kieliopin määräämä syntaksi: sallittujen merkkijonojen rakenne, formaali kuvaus

Tietorakenteet ja algoritmit

Testausdokumentti. Kivireki. Helsinki Ohjelmistotuotantoprojekti HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos

7/20: Paketti kasassa ensimmäistä kertaa

Ohjelmiston testaus ja laatu. Ohjelmistotekniikka elinkaarimallit

13. Loogiset operaatiot 13.1

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Taulukot. Jukka Harju, Jukka Juslin

Tieto- ja tallennusrakenteet

Algoritmit 1. Luento 3 Ti Timo Männikkö

Todistusmenetelmiä Miksi pitää todistaa?

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

Ohjelmistojen mallintaminen, mallintaminen ja UML

Ohjelmistojen mallintaminen. Luento 11, 7.12.

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Tietorakenteet ja algoritmit

ITKP102 Ohjelmointi 1 (6 op)

UML-kielen formalisointi Object-Z:lla

Algoritmit 1. Luento 4 Ke Timo Männikkö

18. Abstraktit tietotyypit 18.1

Ohjelmointiharjoituksia Arduino-ympäristössä

ELM GROUP 04. Teemu Laakso Henrik Talarmo

Tietorakenteet ja algoritmit - syksy

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Ohjelmoinnin jatkokurssi, kurssikoe

List-luokan soveltamista. Listaan lisääminen Listan läpikäynti Listasta etsiminen Listan sisällön muuttaminen Listasta poistaminen Listan kopioiminen

Copyright by Haikala. Ohjelmistotuotannon osa-alueet

Testaaminen ohjelmiston kehitysprosessin aikana

Johdanto II. TIE303 Formaalit menetelmät, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos.

Pino S on abstrakti tietotyyppi, jolla on ainakin perusmetodit:

Rekursiiviset tyypit

Rekursiolause. Laskennan teorian opintopiiri. Sebastian Björkqvist. 23. helmikuuta Tiivistelmä

Kääreluokat (oppikirjan luku 9.4) (Wrapper-classes)

Matematiikan tukikurssi, kurssikerta 1

Käyttötapausanalyysi ja testaus tsoft

Verifioinnin ja validoinnin ero. 7. Verifiointi ja validointi. Verifiointi- ja validointitekniikat. Verifiointi- ja validointitekniikat II

Harjoitustyö: virtuaalikone

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

Johdatus rakenteisiin dokumentteihin

UCOT-Sovellusprojekti. Testausraportti

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Abstraktit tietotyypit. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Sisältö. 2. Taulukot. Yleistä. Yleistä

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. IX Suunnittelumallit Proxy, Factory Method, Prototype ja Singleton

Tietueet. Tietueiden määrittely

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Suunnitteluvaihe prosessissa

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

TIETORAKENTEET JA ALGORITMIT

Johdantoluento. Ohjelmien ylläpito

Matematiikan tukikurssi

13. Loogiset operaatiot 13.1

Luonnollisten lukujen ja kokonaislukujen määritteleminen

Luku 3. Listankäsittelyä. 3.1 Listat

Sisältö. 22. Taulukot. Yleistä. Yleistä

SEPA diary. Dokumentti: SEPA_diary_PK_HS.doc Päiväys: Projekti: AgileElephant Versio: V0.3

Ohjelmointi 2 / 2010 Välikoe / 26.3

Uudelleenkäytön jako kahteen

2. Olio-ohjelmoinista lyhyesti 2.1

Algoritmit 2. Luento 2 To Timo Männikkö

Tietorakenteet ja algoritmit

Harjoitustyön testaus. Juha Taina

811120P Diskreetit rakenteet

Logiikka 1/5 Sisältö ESITIEDOT:

Testausraportti. Orava. Helsinki Ohjelmistotuotantoprojekti HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos

Reaalilukuvälit, leikkaus ja unioni (1/2)

Alityypitys. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Ohjelmoinnin perusteet, syksy 2006

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Geneeriset tyypit. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos

Approbatur 3, demo 1, ratkaisut A sanoo: Vähintään yksi meistä on retku. Tehtävänä on päätellä, mitä tyyppiä A ja B ovat.

Yksikkötestaus. import org.junit.test; public class LaskinTest public void testlaskimenluonti() { Laskin laskin = new Laskin(); } }

Algoritmit 2. Luento 2 Ke Timo Männikkö

Matematiikan johdantokurssi, syksy 2016 Harjoitus 11, ratkaisuista

Ohjelmistojen mallintaminen

Testiautomaatio tietovarastossa. Automaattisen regressiotestauksen periaate ja hyödyt

Automaattinen yksikkötestaus

Tietojen syöttäminen ohjelmalle. Tietojen syöttäminen ohjelmalle Scanner-luokan avulla

Java-kielen perusteet

Algoritmit 1. Demot Timo Männikkö

Täydentäviä muistiinpanoja laskennan rajoista

Sisäänrakennettu tietosuoja ja ohjelmistokehitys

Tietorakenteet ja algoritmit

TIEA341 Funktio-ohjelmointi 1, kevät 2008

ITKP102 Ohjelmointi 1 (6 op)

TT00AA Ohjelmoinnin jatko (TT10S1ECD)

Osoitin ja viittaus C++:ssa

Onnistunut Vaatimuspohjainen Testaus

Ohjelmistoarkkitehtuurit Syksy 2009 TTY Ohjelmistotekniikka 1

Algoritmit 1. Luento 10 Ke Timo Männikkö

Sisäänrakennettu tietosuoja ja ohjelmistokehitys

Soveltuvuustutkimus Lifebelt-ohjelman ideologian käytettävyydestä olioorientoituneeseen

Ohjelmointi 1 Taulukot ja merkkijonot

The OWL-S are not what they seem

Datatähti 2019 alku. task type time limit memory limit. A Kolikot standard 1.00 s 512 MB. B Leimasin standard 1.00 s 512 MB

Transkriptio:

Formaalit menetelmät ja testaus: Abstraktien tietotyyppien käyttö yksikkötestauksessa Tommi Kärkkäinen JYVÄSKYLÄN YLIOPISTO Informaatioteknologian tiedekunta Tietotekniikan laitos syksy 2006

Yleistä formaaleista menetelmistä Tässä luvussa esitellään formaaleihin menetelmiin liittyviä yleisiä asioita (lähinnä) lähteen [NASA] NASA RELEASE NOTES, Formal Methods Specification and Verification Guidebook for Software and Computer Systems; Volume I: Planning and Technology Insertion, NASA, Washington,! " #$% &" '()* +," -" 1998 pohjalta Käyttötarkoitukset Formaalien menetelmien käyttötarkoitus ohjelmistosuunnittelussa on pääasiassa ohjelmien vaatimusmäärittelyn toteuttaminen mahdollisimman selkeällä ja yksikäsitteisellä tavalla Tähän voi myös liittyä ohjelman toiminnan oikeellisuuden verifiointi eli varmistaminen joko teoreettisesti ja/tai kokeellisesti usein olemassaolevien työkalujen avulla Ohjelmistosuunnittelussa formaali lähestyminen on erityisen tärkeää rakenteeltaan monimutkaisia ja turvallisuuskriittisiä (ydinvoimaloiden ohjausjärjestelmät, sairaalavimpaimet, yms) järjestelmiä kehitettäessä Formaalin ohjelmankehityksen yhtenä ääritapauksena voidaan pitää ns Dijkstralaista koulukuntaa, jonka mukaan ohjelmalle annetut vaatimukset mallinnetaan matemaattisesti käyttäen jotain aksiomaattista menetelmää ja ohjelman toiminta todistetaan oikeaksi ennenkuin tehdään ensimmäistäkään riviä koodia! Etuja Formaali ajatusmalli Ohjelman toiminta kuvataan täsmällisesti ennen implementointia voi vain auttaa tulemaan paremmaksi ohjelmistokehittäjäksi tai testaajaksi! (vaikka et sitä pilkulleen noudattaisikaan) Käytettäessä formaaleja menetelmiä spesifikaatio pyritään tekemään täsmällisesti ja yksikäsitteisesti jo ohjelmiston elinkaaren alkupuolella, analyysi-/suunnitteluvaiheessa Siksi määrittelyn sisältämiä epätäsmällisyyksiä ja varsinaisia virheitä voidaan havaita jo elinkaaren tässä vaiheessa - toisin kuin käytettäessä epätäsmällisempiä menetelmiä Lisäksi on olemassa paljon esimerkkejä tapauksista, joissa formaalien menetelmien käyttö on paljastanut jo toiminnassa olevista ohjelmistoista virheitä, joita ei ole huomattu kehitys- ja testausvaiheissa Kun ohjelma tila ja sen muutokset on määritelty tarkasti, voidaan ohjelman toimintaa käydä läpi eli simuloida ja (esim tilastollisesti) testata täsmällisesti Simuloinnissa 2

etsitään erityisesti tilanteita, joissa jokin ohjelmalle etukäteen määrätty rajoite ei ole voimassa eli ohjelma toimii väärin Parhaassa tapauksessa voidaan jopa todistaa matemaattisesti, että ohjelma toimii eli tekee sen, mitä vaatimusmäärittely edellyttää Formaalien menetelmien avulla voidaan löytää määrittelyssä esiintyviä ristiriitoja varmemmin ja enemmän Lisäksi joissain tapauksissa voidaan taata (osoittaa), että ohjelma ei käyttäydy (tosin, NASA:ssa näitä tekniikoita sovelletaan ja avaruusteleskoopit ja -sukkulat senkun reistailevat ja avaruusluotaimet hukkuvat viimeistään määränpäässään ;-) Formaali mallitus Ohjelmistojen formaalilla mallituksella tarkoitetaan prosessia, jossa systeemin ei-matemaattinen, sanallinen tai vaikkapa tietorakenteiden ja objektien muutoksia kuvaava diagrammiesitys muunnetaan matemaattiseksi esitykseksi Formaaliin mallitukseen liittyviä perustermejä ovat mm Formaali määritys: ohjelmiston ominaisuuksien ja toiminnan kuvaaminen epätäsmällisten ilmauksien sijasta matemaattisten rakenteiden avulla mahdollisimman abstraktilla tasolla Formaali todistus: matemaattinen, usein työkaluavusteisesti kehitetty todistus sille, että ohjelma toimii vaatimusten määräämällä tavalla Abstrahointi: ohjelmiston toiminnan kuvaaminen mahdollisimman yleisellä tasolla, jossa tarkemmat yksityiskohdat määritetään vasta kehitysprosessin myöhemmässä vaiheessa Vaatimusten animointi (emulointi, simulointi): Formaalia määritysprosessia tuetaan usein ohjelmankehitystyökaluilla, joiden avulla määritys muutetaan toimivaksi ohjelmaksi ja järjestelmän toimintaa jäljitellään tämän avulla Tällaisiin malleihin on suoraviivaista lisätä ohjelman tilaa ja sen muutoksia kuvaavia testitapauksia, joita voidaan käyttää esim (osana) regressiotestauksessa Formaali mallitus voi muodostua esimerkiksi seuraavien askeleiden kautta: Formaali määritys Tyyppien tarkistus: Ohjelman sisältämille muuttujille valittujen tyyppien oikeellisuuden varmistaminen (tyypit ja niiden muunnokset (tai näiden puuttuminen) ovat tyypilliä virhelähteitä heikosti tyypitetyille kielille) Ominaisuuksien määritys: Systeemin halutun toiminnan identifiointi ja (formaali) määrittely (ominaisuuksien ja toiminnan testaus) Loogisten ehtojen todistus: Systeemin haluttua toimintaa kuvaavien loogisten lausekkeiden todistaminen oikeaksi (ja varmennus testaamalla)

Formaalit menetelmät projektin osana Kun formaaleja menetelmiä on tarkoitus soveltaa jossakin ohjelmistoprojektissa, täytyy ottaa huomioon ainakin seuraavia asioita: Henkilökunta: Formaalien menetelmien yleistymisen suurin este on koulutetun henkilökunnan puute Koska menetelmien soveltaminen vaati aikalailla matemaattisia pohjia, on erityisesti jo alalle työskentelevien henkilöiden kouluttaminen aikaavievä projekti, joka yrityksissä usein jätetään tuottavuussyistä tekemättä ja ummistetaan raakasti silmät koko asialta Ei meillä ole ennenkään tuollaista tarvittu Prosessin integrointi: Olemassaolevian määrittelytapojen muuttaminen formaaliin suuntaan vaatii toisaalta hyvän yleisnäkemyksen alasta sekä myös tietoa menetelmien hyödyistä/rajoitteista, jotta ohjelmistoprosessia voidaan muokata hallitusti vanhoja tapoja uudistamalla ja parantamalla Projektin suunnittelu: Ne ohjelmistoprojektin osat, joissa formaalien menetelmien soveltamisesta saadaan paras mahdollinen hyöty, pitää pystyä tunnistamaan (elinkaaren mahdollisimman aikaisessa vaiheessa) Ohjelmistotyyppi: Useissa tapauksissa jokin muu, jo olemassaoleva tapa toteuttaa ohjelmistoprojekteja toimii tarpeeksi hyvin ilman sen kummempia muutoksia Erityisesti laajojen ohjelmistoprojektien täydellinen formalisointi voi tukehduttaa koko projektin tai ainakin viedä liikaa (työnantajalle) kallista aikaa Usein tyydytäänkin ratkaisuun, jossa tarvittaessa ohjelmiston kriittisimmät osaratkaisut esitetään formaalisti Lisäksi erityyppisten systeemien rakentamiseen soveltuvat luonnollisesti erilaiset työkalut, joten yhteen ratkaisuun kiinnittyminen voi pitkässä juoksussa tuoda hankaluuksia Formaalien menetelmien haittoja Listataanpas vielä osittain jo aikaisemmin mainittuja tekijöitä, jotka monesti estävät formaalien menetelmien soveltamisen: hankalia käyttää, vaativat liian paljon pohjatietoja liian vähän hyviä ja toisaalta liian paljon erilaisia työkaluja, joiden kaikkien hallitseminen liian työlästä yhdelle ihmiselle/projektiryhmälle/yritykselle ilman CASE-työkaluja osa tehosta menetetään sovelletaan, mutta esitietojen hallitsemattomuuden vuoksi väärin formaali spesifikaatio pitää erikseen suomentaa asiakkaalle (mutta niiden hyödyntäminen testauksessa tarjoaa mielenkiintoisia mahdollisuuksia, kts kuva 1) niin laaja alue, että niihin liittyen on mahdotonta (onko mikään?) muodostaa selkeää luentokokonaisuutta ;-)

! (laatu) Testi(tapaus)! (täsmällisyys) Asiakas? Formaali Speksi Kuva 1: Formaalin spesifikaation rooli testauksessa Formaalien menetelmien sovelluskohteita Lopuksi listataan jo osittain aikaisemmin mainittuja formaalien menetelmien pääasiallisia sovelluskohteita: turvallisuuskriittiset järjestelmät: ydinvoimaloiden prosessinohjaus, lentokoneen ohjaus, sairaalalaitteistot, hankalasti korjattavat järjestelmät: avaruusluotaimet, piilastut, puhelinverkon protokollat, (virheiden korjaus joko mahdotonta tai älyttömän kallista, virheet nakertavat organisaation imagoa ja tiputtavat/romahduttavat pörssiarvon) reaktiiviset järjestelmät: monen käyttäjän järjestelmät, tietoliikenne, (rinnakkaisuuden hallinta ihmiselle vaikeaa, sopivasti ilmaistuna koneelle helppoa) 7+7 myyttiä ja 10 käskyä Formaaleista menetelmistä on 1960-luvulta lähtien käyty osin kiihkeätäkin keskustelua ohjelmistotekniikan piirissä Tämä keskustelu on suuresti vaikuttunut artikkeleista [Hal90] A HALL, Seven Myths of Formal Methods, IEEE Software, vol 7(5), pp 11 20, 1990, [BH95] J P BOWEN AND M G HINCHEY, Seven More Myths of Formal Methods, IEEE Software, vol 12(4), pp 34 41, 1995, joihin on koottu yhteensä 7+7 formaaleihin menetelmiin liitettyä "myyttiä"eli uskomusta eli syytä sille, miksi menetelmiä ei pidä ottaa vakavasti: 1 Formaalit menetelmät takaavat, että ohjelmisto on täydellinen 2 Formaalit menetelmät liittyvät pelkästään ohjelman oikeaksi todistamiseen 3 Formaalit menetelmät ovat tarpeellisia vain turvallisuuskriittisissä järjestelmissä 4 Formaalit menetelmät vaativat korkeasti koulutettuja matemaatikkoja soveltajikseen 5 Formaalit menetelmät kasvattavat kehityskustannuksia 6 Formaalit menetelmät eivät sovi asiakkaille

7 Formaaleja menetelmiä ei käytetä oikeiden, laajojen ohjelmistojen suunnittelussa 8 Formaalien menetelmien käyttö hidastaa ohjelmistoprosessin läpimenoaikaa 9 Formaalien menetelmien tueksi ei ole olemassa työkaluja 10 Formaalit menetelmät korvaavat perinteiset suunnittelumenetelmät 11 Formaalit menetelmät soveltuvat vain ohjelmistosuunnitteluun 12 Formaaleja menetelmiä ei tarvita 13 Formaaleja menetelmiä ei tueta 14 Formaalien menetelmien soveltajat käyttävät aina formaaleja menetelmiä Jälkimmäistä artikkelia seuranneessa julkaisussa [BH95b] J P BOWEN AND M G HINCHEY, Ten Commandments of Formal Methods, IEEE Computer, vol 28(4), pp 56 63, 1995, muotoiltiin sitten kymmenen formaaleihin menetelmiin liittyvää käskyä raamatullisessa hengessä ("Thou shalt"; käännös poikkeaa alkuperäisestä esityksestä (suom huom)): Käytä soveltuvaa notaatiota: Jokainen spesifiointimenetelmä on tarkoitettu tietyntyyppisten käsitteiden esittämiseen, ja luonnollisen sovellusalueensa ulkopuolella esityksistä tulee yleensä kohtuuttoman monimutkaisia ja hankalasti ymmärrettäviä Tyypillisenä esimerkkinä mainittakoon taas mallipohjaiset Z ja VDM sekä rinnakkaisuuden esittämisen vaikeus Muistetaan myös, että jonkun oudon notaatioläjän syntaksin ja semantiikan opettelu tarkasti on vaativa prosessi - erityisesti eri roolien (kehittäjä, asiakas, manageri) näkökulmasta Formalisoi, mutta älä yliformalisoi: Tosiasia on, että todella laajojen järjestelmien täydellinen formaali mallittaminen ja määrittäminen ei ole vielä nykypäivänä mahdollista tai edes tarpeellista Formaalit menetelmät liittyvät intiimisti järjestelmän toiminnallisuuteen, joten esim käyttöliittymän visuaalisen olemuksen kuvaamiseen ja testaamiseen kannattaa käyttää muita, käyttäjälähtöisempiä menetelmiä Siis (jos osaa) kannattaa soveltaa, mutta vain tarvittaessa Arvioi kulut: Tarvittavan osaamispääoman luominen yritykseen vaatii yleensä henkilöstön lisäkoulutusta, jonka kulut on pystyttävä kattamaan Lisäksi formaalejakin menetelmiä soveltavan asiakasprojektin kulut on pystyttävä arvioimaan etukäteen, ja tämä vaatii tietoa jo toteutuneista projekteista Tämmöistä tietoa on kuitenkin edelleenkin hyvin rajoitetusti saatavilla, joten kulujen arviointiin liittyvää kokemusta on vähän Hanki guru: Yhteistä onnistuneille formaaleja menetelmiä soveltaneille projekteille on ollut projektihenkilökunnan mahdollisuus korkealaatuiseen ammatilliseen ohjaukseen todellisen asiantuntijan toimesta Älä hylkää perinteisiä menetelmiä: Integroi (Tästä on puhuttu ja kirjoitettu jo tarpeeksi montaa kertaa tähän mennessä!) Dokumentoi riittävästi: Formaalit määritykset tulee täydentää dokumentoinnissa esitysmuodoilla, joita kaikki eri osapuolet ymmärtävät

Säilytä laatustandardit: Formaalit menetelmät eivät (yleensä, välttämättä) takaa ohjelman oikeellisuutta, joten muita laadun varmistusmenetelmiä (testaukset, katselmukset jne) ei pidä unohtaa tai poistaa käytöstä Älä ole dogmaattinen: Formaalit(kaan) menetelmät eivät ole ohjelmistokehityksen "hopealuoti" Esimerkiksi prosessia, jossa epäformaalit vaatimukset muutetaan formaaleiksi, ei voi itseään todistaa oikeaksi, joten ohjelmistoprosessin validoimiseksi tarvitaan muitakin menetelmiä (kts edellinen kohta) Lisäksi formaaleja menetelmiä tukevien työkalujen luotettavuutta on mahdoton todistaa Testaa, testaa, testaa: Ihmisten suunnittelemista, määrittelemistä ja toteuttamista ohjelmista löytyy aina bugeja ja virheitä, joten formaalit(kaan) spesifikaatiot eivät koskaan korvaa kattavan testauksen tarpeellisuutta Uudelleenkäytä: Formaali spesifikaatio perustuu korkeaan abstraktiotasoon ja täsmälliseen määritykseen, jonka vuoksi kerran kunnolla tehtynä se tarjoaa hyvät uudelleenkäyttömahdollisuudet sekä projektin sisällä että muissa projekteissa

Abstraktit tietotyypit ja yksikkötestaus Kappale pohjautuu (pääosin) lähteeseen [Vliet] H VAN VLIET, Software Engineering: Principles and Practice (2nd edition), John Wiley & Sons, New York, 2000 Tässä yhteydessä yksityiskohdat (erityisesti terminologia) ei ole merkityksellisessä roolissa, sillä päätarkoituksena on antaa konkreettinen tuntuma täsmällisten spesifikaatioiden olemuksesta ja käytöstä yksikkötestauksen suunnittelun apuna IEEE Standard Glossary of Software Engineering Terminology:n (1990) mukaan testitapaus (test case) määritellään (jotakuinkin) seuraavasti: Testitapaus muodostuu joukosta testisyötteitä, suoritusehtoja ja tavoiteltavia tuloksia, joiden avulla voidaan esim tarkastaa, toimiiko ohjelma tiettyjen määritysten mukaisesti Mistä nämä testitapaukset tulevat ja miten ne saadaan muotoiluiltaan sellaisiksi, että ei testata lillukanvarsia (Int: 1+1 <> 3, i+2 = 3)? Abstrakti tietotyyppi (abstract data type, ADT) on rakenteensa ja operaatioidensa suhteen täsmällisesti kuvattu ohjelmistokomponentti, joka on käytännössä hyvin läheistä sukua abstraktille luokalle (terminologian outouden ja ehkäpä hieman ristiriitaisuudenkin selittää se, että tässä esitettävät kuvaukset olivat käytössä jo ennen kuin olioparadigman valtakausi alkoi; itse asiassa ADT:t ovat vahvasti koko paradigman taustalla) Kappaletta läpikäydessä kannattaakin esitettyjä asioita koko ajan verrata luokkaan, sen rakenteeseen, rakenteen määrittämiseen ja luokan (rajapinnan) yksikkötestaukseen! ADT koostuu joukosta arvoja, "sortteja"(engl sort; ei taaskaan löydy hyvää suomennosta, mutta käytännössä nämä voidaan samaistaa luokan atribuutteihin), sekä joukosta operaatioita, joita voidaan kohdistaa "sortteihin"(metodit; vrt saantimetodit, tiedon piilotus, ei suoraa yhteyttä luokan atribuutteihin) Tyypillisiä esimerkkejä ADT:iden avulla määriteltävissä olevista tietorakenteista ovat vaikkapa: pino esim operaatioilla Create, Top, Pop, Push binääripuu esim operaatioilla Create, Insert, IsIn hierarkkinen taulukko esim operaatioilla Create, IncrLevel, DecrLevel, Add, IsIn, Retrieve Geneeriseksi rakenteet tekee se, että alkioiden tyyppiä muuttamalla sama toiminnallisuus voidaan kohdistaa esim int, float, string yms eri pinoille, puille ja taulukoille Lisäksi ADT:iden määrityksissä ei oteta mitään kantaa siihen, millä ohjelmointikielellä rakenne todellisuudessa implementoidaan, kunhan implementointi tarjoaa määritetyt operaatiot sitä 8

käyttävässä ohjelmassa valitulle tyypille Tämä on hyödyllistä esim koodigeneraattoreita ja korkeamman tason ohjelmointiympäristöjä rakennettaessa ADT:n määrityksessä ilmoitetaan sekä operaatioiden syntaksi (muoto) että niiden semantiikka (tulkinta) Mallipohjaisissa formaaleissa määrityksissä semantiikka kerrotaan eksplisiittisesti, mutta algebrallisessa spesifikaatiossa semantiikka esitetään implisiittisesti sitomalla määriteltyjen operaatioiden toiminta toisiinsa sopivien sääntöjen, aksioomien, avulla Otetaan esimerkiksi kokonaislukupinon algebrallinen määritys: type IntStack; functions Create : IntStack Push : IntStack Int IntStack Pop : IntStack IntStack Top : IntStack Int IsEmpty : IntStack Boolean axioms IsEmpty(Create) True IsEmpty(Push(s i)) False Pop(Create) Create Pop(Push(s i)) s Top(Create) 0 Top(Push(s i)) i end IntStack; Ja sitten muutamia huomioita ja kommentteja määrityksen suhteen: Pinon määrityksessä oletetaan, että ADT:t Boolean ja Int ovat jo valmiiksi määriteltyjä ja True ja False ovat Boolean-tyyppisiä vakioita Etsi-ja-korvaamalla pinon tyypin ilmaiseva Int yleiseen muotoon ( T ) tekee rakenteesta tyyppiriippumattoman Operaatioiden määritelmissä vastaa input-parametrilistan pilkkua ja :n jälkeen annetaan output:n tyyppi Pinon aksioomilla, joista jokainen on identiteetti, on seuraavat tulkinnat: 1 Create luo (alustaa) tyhjän pinon 2 pinoon lisääminen (Push) tekee pinosta välttämättä epätyhjän 3 tyhjästä nyhjääminen antaa edelleen tyhjän 4 pinoon lisääminen ja poistaminen antaa alkuperäisen pinon 5 Top täytyy määritellä erikseen tyhjälle pinolle (vrt ekvivalenssiluokat ja niiden rajat) palauttamaan jonkun oikeaa tyyppiä olevan arvon, vaikkapa nollan (tämä ei luonnollisesti riitä määrittämään tyhjää pinoa, koska luku "0"voi olla pinossa muutenkin) 6 pinoon lisäämisen jälkeen sen päällimmäisenä on lisätty luku Kaikkia operaatioita (katso erityisesti Create) on kuvattu niillä olevien ominaisuuksien ("mitä"), ei implementoinnin ("miten") kannalta (vrt mihin testauksella pyritään )

Annettu spesifikaatio vastaa hyvin konkreettista implementaatiota, jossa tyyppi IntStack vastaa kokonaislukuosoitinta ja esim Create palauttaa tyhjän osoittimen (siis lista) Toisaalta, aivan yhtä hyvin pino voidaan realisoida samaistamalla IntStack kokonaislukuvektorin kanssa (siis taulukko) Tällöin on luonnollista liittää ADT:hen mukaan tieto pinon koosta ja maksimikoosta, ja käyttää kokomuuttujaa esim tyhjän pinon indikaattorina Kuten jo aikaisemmin mainittiin, ADT:n alkuosa type- ja functions-klausien (avainsanojen) alaisuudessa vastaa suunnilleen normaalin luokan määrittelyä Tästä näkökulmasta aksioomissa esitettyjen ehtojen avulla voidaan luokan toteutusta yksikkötestata Erilaisten aksioomien tunnistaminen ja muodostaminen antaa näin ollen hyvät eväät myös yksikkötestauksen suunnitteluun ja toteutukseen (ilman, että termiä "formaali"tarvitsee edes mainita - ainakaan pomolle ;-) Laajoissa määrityskirjastoissa modulaarista rakennetta voidaan tukea esim seuraavasti: type Stack[Item]; imports True, False from Boolean, = from Item; exports Create, Push, Pop, Top, IsEmpty; Tämä määrittää ADT:n rajapinnan muihin ADT:ihin nähden (vrt esim Javan Interface) Huomaa, että imports- ja exports-listojen kautta kontrolloidaan operaatioita (lue: metodeja), joita toiset ADT:t käyttävät (vrt koostaminen ja yhden ADT:n sisältämien metodien näkyvyyden määrittäminen) Pinon algebrallinen spesifikaatio on loppujen lopuksi kohtuullisen suoraviivaista luettavaa, mutta laajojen tämänmuotoisten tyypinmäärityskirjastojen oikeellisuuden, kattavuuden ja ristiriidattomuuden tutkiminen on melkoisen matemaattista puuhastelua Tämän johdosta tarkastellaan myös jatkossa yksinkertaisiin esimerkkeihin liittyviä asioita Alla onkin annettu luonnollisten lukujen algebrallinen määritys (tämän esittäminen näissä yhteyksissä on lähes de facto -standardi, joka sitten kuitenkin johti lillukanvarsiin ;-): 1 type Nat; 2 functions 3 Null : Nat 4 Succ : Nat Nat 5 Add : Nat Nat Nat 6 axioms 7 Add(i Null) i 8 Add(i Succ(j)) Succ(Add(i j)) 9 end Nat; Ja sitten taas kommentteja määrityksen suhteen, johon rivinumerot on lisätty pelkästään selvyyden vuoksi: Määrityksen perustulkinta on selvä: Null on nolla-alkio, Succ viittaa annettua luonnollista lukua seuraavaan lukuun (Succ(1) 2 Succ(2) 3 jne) ja Add on luonnollisten lukujen yhteenlaskuoperaatio Määritys jakaantuu luonnollisesti kahteen osaan:

signature-osassa (allekirjoitus) riveillä 1-5 luetellaan "sortit"(näitä voi olla useampiakin type-avainsanan jälkeen) sekä näihin kohdistuvat funktiot tyyppeineen (Null on tyyppiä Nat oleva vakio, funktio Succ ottaa inputiksi ja myös palauttaa tyyppiä Nat olevan alkion, Add kohdistuu kahteen tyyppiä Nat olevaan alkioon ja palauttaa yhden samantyyppisen elementin) Esitysjärjestys määräytyy yleensä lähtöjoukon laajuuden mukaan equation-osassa (identiteetit) riveillä 6-8 listataan allekirjoituksessa esitettyjä vakioita ja funktioita sitovat ja yhdistävät päättelysäännöt, joiden täytyy olla voimassa tyypin varsinaisessa implementaatiossa Annetuissa esimerkeissä on rajoituttu pelkästään identiteetteihin, mutta yleensä aksioomissa voidaan käyttää myös epäyhtälöitä ja ehto-lauseita Allekirjoitus voidaan nähdä kontekstittomana (määrittely-ympäristöstään riippumattomana) kielioppina lausekkeille, jotka sen avulla määritellään Esimerkiksi luonnollisten lukujen määrittely sallii syntaksia Null Succ(Succ(Add(Succ(Null) Succ(Null)))) Add(Succ(Null) Succ(Succ(Null))) i Add(Succ(i) j) olevien lausekkeiden esittämisen Näistä kolmen ensimmäisen sanotaan olevan suljettuja (closed), koske ne sisältävät pelkästään vakioita (Null) ja funktioita Muuttujia i ja j sisältävien kahden viimeisen lausekkeen sanotaan olevan avoimia (Pilkuntarkasti: syntaksin oikeellisuuden tarkistamiseksi meidän tulisi tietää myös, mitkä ovat avoimissa lausekkeissa käytettäville muuttujille sallitut merkinnät) Täsmällisesti ilmaistuna allekirjoitus annetulle tyypille T määrittelee sallittujen lausekkeiden joukon seuraavien sääntöjen mukaan: 1 Tyyppiä T olevat vakiot ja muuttujat ovat tyypin peruslausekkeita 2 Jos l 1 l n ovat tyyppiä t 1 t n olevia lausekkeita ja f on funktio tyyppiä t 1 t n olevian muuttujien joukosta tyyppiä T olevien muuttujien joukkoon, niin f (l 1 l n ) on tyyppiä T oleva lauseke Tämä tekee algebrallisesta spesifikaatiosta täysin formaalin systeemin, jonka muutokset perustuvat identiteetti-osassa annettujen päättelysääntöjen käyttämiseen uusien lausekkeiden muodostamiseksi "Sortin"Nat määritelmän perusteella esimerkiksi seuraavanlainen päättelyketju on täysin sallittu (ja todistaa aukottomasti, että 1+2=3 on validi testitapaus ;-): Add(Succ(Null) Succ(Succ(Null))) (sääntö rivillä 8) Succ(Add(Succ(Null) Succ(Null))) (sääntö rivillä 8) Succ(Succ(Add(Succ(Null) Null))) (sääntö rivillä 7) Succ(Succ(Succ(Null)))

Kuinka rakentaa algebrallinen ADT? ADT:n määräämisen suurin hankaluus on jo edeltä käynyt selväksi: kuinka löytää rakenteelle sopiva tulkinta eli sopiva semantiikka eli sopiva joukko rakennetta sitovia päättelysääntöjä? (Vertaapa tätä kattavan mutta minimimääräisen testitapausten joukon muodostamisen vaikeuteen) Käydään seuraavassa läpi jotain yleisiä suuntaviivoja tutunoloisen esimerkin valossa type IntStack; functions Create : IntStack Push : IntStack Int IntStack Pop : IntStack IntStack Top : IntStack Int Size : IntStack Int IsEmpty : IntStack Boolean Tässä uusi muuttuja Size kertoo pinon kyseisellä hetkellä sisältämien kokonaislukujen määrän eli pinon koon Annetut funktiot voidaan jakaa 2*2 = neljään eri luokkaan: 1 Konstruktorit (constructors): Funktiot, joiden arvojoukkona (f : X Y :lle X on lähtö- eli määrittelyjoukko ja Y on tulo- eli arvojoukko) on määriteltävänä oleva tyyppi, esimerkissä IntStack 11 Peruskonstruktorien avulla muodostetaan kaikki ADT:n määrittämät tietorakenteet Esimerkissä peruskonstruktoreja ovat Create ja Push, joiden avulla voidaan rakentaa mikä tahansa pino puhtaalta pöydältä 12 Ylimääräisten konstruktorien avulla voidaan muuttaa määritettävää rakennetta; esimerkissä Pop 2 Havainnoijat (observers): Funktioita, jotka kohdistuvat johonkin muuhun tyyppiin kuin määriteltävään rakenteeseen 21 Perushavainnoijat ovat primitiivisiä rakenteen "tutkijoita", joita ei voida ilmaista muiden havainnoijien avulla Esimerkissä Top ja Size ovat perushavainnoijia, sillä IsEmpty(s) on ekvivalentti (= tarkoittaa samaa eli sillä on sama tulkinta) Size(x) 0 :n kanssa 22 Ylimääräiset havainnoijat ovat havainnoijia, jotka eivät ole perushavainnoijia; esimerkissä siis IsEmpty Jotta algebrallisesti määritellyllä ADT:llä olisi halutut ominaisuudet, täytyy spesifikaation olla tarpeeksi kattava Esitetään lopuksi heuristiikka, joka (yleensä) johtaa tarpeeksi kattavaan joukkoon funktioita ja niitä sitovia aksioomia: 1 Määritä peruskonstruktorit; esimerkissä Create ja Push 2 Määritä ylimääräiset konstruktorit, jotka siis käytännössä "vähentävät"rakennetta - Pop Lisää jokaiselle ylimääräiselle konstruktorille sen kaikkiin peruskonstruktoreihin liittämä, muotoa Pop(Create)

ja Pop(Push(s i)) oleva aksiooma Identiteettien oikea puoli täytyy itse osata kehittää semanttisesti järkeväksi Tarkasti tämä vaatii aksiooman todistamisen oikeaksi jo muodostettujen aksioomien suhteen edellä luonnollisille luvuille esitetyn päättelyketjun mukaisesti, mutta meille riittää hakea täsmällisessä esitysmuodossa vastaukset kysymyksiin Pop(Create) "Mitä tapahtuu, kun tyhjästä pinosta yritetään napata alkio ja muistetaan, että operaation tuloksen tyypin pitää myös olla pino?" ja Pop(Push(s i)) "Mitä tapahtuu, kun pinoon lisätään ja poistetaan alkio?" 3 Myös jokaiseen perushavainnoijaan liitetään aksioomat, jotka liittävät ne peruskonstruktoreihin: Top(Create) Top(Push(s i)) Size(Create) Size(Push(s i)) "Mikä alkio saadaan nyhjäistyä tyhjän pinon päältä?" "Mikä alkio löytyy pinon päältä, kun sinne on lisätty alkio i?" "Minkä kokoinen on tyhjä pino?" "Mitä tapahtuu pinon koolle, kun sinne lisätään alkio i?" 4 Lopuksi jokainen ylimääräinen havainnoija liitetään sopivan aksiooman avulla perushavainnoijiin: IsEmpty(s) "Mikä ehto pinon koon suhteen kuvaa tyhjää pinoa?" Näin muodostetun aksioomajärjestelmän "riittävyys"on siis vaikea kysymys (Gödel) Yleensä on syytä tarkastella myös niitä tyyppiyhteensopivia tapauksia, joissa muodostettu aksiooma voidaan esittää myös käänteisessä järjestyksessä tyyliin Push(s Pop(s)), Push(s Top(s)) jne