Esittely latautuu. Ole hyvä ja odota

Esittely latautuu. Ole hyvä ja odota

Komponenttipohjainen ohjelmistotekniikka (TJTSS56) Osa 4 Kevätlukukausi 2010 Jyväskylän yliopisto Tietojenkäsittelytieteiden laitos Markku Sakkinen.

Samankaltaiset esitykset


Esitys aiheesta: "Komponenttipohjainen ohjelmistotekniikka (TJTSS56) Osa 4 Kevätlukukausi 2010 Jyväskylän yliopisto Tietojenkäsittelytieteiden laitos Markku Sakkinen."— Esityksen transkriptio:

1 Komponenttipohjainen ohjelmistotekniikka (TJTSS56) Osa 4 Kevätlukukausi 2010 Jyväskylän yliopisto Tietojenkäsittelytieteiden laitos Markku Sakkinen

2 Yhteys rinnakkaisuuteen Osittain Szyperski, kohta 5.9 Vapaakäyntisyys on oleellinen asia rinnakkaisissa ohjelmissa. Varsinkin säikeissä eli kevyissä prosesseissa, jotka toimivat yhteisessä osoiteavaruudessa. Olioiden (tms.) lukitsemista käytetään usein estämään rinnakkaisia säikeitä käyttämästä samaa oliota yhtä aikaa – siis estämään säikeiden välinen vapaakäyntisyys. Säie pääsee kuitenkin tulemaan rekursiivisesti sellaiseen olioon, jonka se on jo lukinnut (esim. Javassa). Muuten yksisäikeisessäkin ohjelmassa voisi tapahtua lukkiutuma (deadlock). Tunnetusti lukkojen käyttäminen voi helposti aiheuttaa lukkiumia kahden tai useamman säikeen kesken. Tietokannan hallintajärjestelmissä voidaan purkaa havaittu lukkiutuma kumoamalla jokin siihen osallinen transaktio ja käynnistämällä se sitten uudestaan. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 16. 2. 2010 2

3 Polymorfisuus Szyperski, luku 6 Korvattavuus (substitutability) Polymorfisuuden kaksi puolta: Sama asia voi näyttäytyä erilaisena eri yhteyksissä. Eri asiat voivat näyttäytyä samanlaisina jossakin yhteydessä. Hyvän itsenäisen rajapinnan määritteleminen on vaikeaa tasapainoilua. Jos asiakkaalta vaaditaan liikaa tai palveluntarjoajalta (palvelimelta) liian vähän, rajapinnan mukainen palvelu ei kiinnosta potentiaalisia käyttäjiä. Päinvastaisessa tapauksessa potentiaaliset toimittajat eivät ole halukkaita toteuttamaan palvelua. Hyvin määritelty rajapinta jättää kummallekin osapuolelle tarpeeksi vapautta. Rajapinta on sopimus, jonka kumpikin osapuoli voi tehdä itse enemmän ja vaatia toiselta vähemmän, kuin sopimus edellyttää. Rajapinnan kullekin operaatiolle määritellään yleensä esi- ja jälkiehto. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 3

4 Korvattavuus (jatkoa) Rajapinnan kullekin operaatiolle määritellään yleensä esi- ja jälkiehto, jotka ovat predikaatteja. Myös se, mitä tietty asiakas ja tietty palvelin vaativat ja takaavat, voidaan ilmaista predikaateilla. Implikaatiosuhteet: (asiakas takaa kutsuessaan)  (rajapinnan esiehto)  (palvelin vaatii kutsuttaessa) (palvelin takaa palatessaan)  (rajapinnan jälkiehto)  (asiakas vaatii kutsun tuloksena) Esimerkki: luokka, joka toteuttaa rajapinnan TextModel. Ei kuitenkaan vaadi, että write -metodin kutsussa lisättävän merkin paikka olisi nykyisen tekstin sisällä tai heti sen lopussa. Muussa tapauksessa lisää tekstin loppuun ensin tarvittavan määrän tyhjämerkkejä. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 4

5 Korvattavuus (jatkoa) Muutokset rajapinnan vastaavaan esi- ja jälkiehtoon on seuraavassa kirjoitettu punaisella. Class GreatTextModel implements TextModel {... void write (int pos, char ch); // [ txt: array of char. // pre (all i : 0 <= i < this.length() | // txt[i] := this.read(i)); // this.length () < this.max () // and 0 <= pos < this.max () // post this.length() = max (old (this.length(), pos) +1 // and (all i : 0 <= i < min (pos, old (this.length()) // | this.read (i) = txt[i]) // and this.read (pos) = ch // and (all i : pos <= i < this.length () : // this.read (i) = txt[i-1]) // and (all i : old (this.length ()) < i < pos | // this.read (i) = ´´ ´´ // ]... }... } Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 5

6 Korvattavuus (jatkoa) Voidaan helposti todeta, että rajapinnassa määritelty esiehto tosiaan implikoi tämän esiehdon. Jälkiehdon on toteuduttava vain siinä tapauksessa, että esiehto on ollut voimassa. Voidaan todeta, että silloin tämä jälkiehto tosiaan implikoi rajapinnassa määritellyn jälkiehdon. Eiffelissä yli- ja aliluokan metodin ehtojen väliset implikaatiosuhteet taataan jo syntaktisilla rajoitteilla: Aliluokassa määritellään vain ”lisäehdot”. Aliluokan metodin efektiivinen esiehto on perityn esiehdon ja lisäesiehdon disjunktio. Metodin efektiivinen jälkiehto on vastaavasti perityn ja lisäjälkiehdon konjunktio. Asiakkaan puolella ei (ainakaan nykyisissä kielissä) ole vastaavia syntaktisia keinoja ilmaista sitä, että luvataan enemmän tai vaaditaan vähemmän, kuin jokin rajapinta määrittelee. Esim. jokin asiakas voisi käyttää write -metodia vain tekstin loppuun lisäämiseen. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 6

7 Korvattavuus (jatkoa) TextModel -rajapinnan toteuttava luokka voi toteuttaa monenlaisia muitakin lisäasioita, esim.: Lisättävän, poistettavan tai luettavan merkin paikaksi kelpuutetaan mikä kokonaisluku tahansa, negatiivinenkin. Todella sovellettava paikka on kuitenkin tekstin alueella. Tekstin pituudella ei ole kiinteää rajaa, vaan tekstille varattua tilaa ja siis myös max - metodin tuloa kasvatetaan tarvittaessa. Lukeminen todellisen tekstin ulkopuoleltakin on mahdollista ja antaa tyhjämerkin. Pelkät esi- ja jälkiehdot eivät ole aivan riittävä sopimus. Palvelin ei toki saa tehdä mitä hyvänsä, jos kutsun esiehto ei täyty. Jos esiehto täyttyy, palvelin ei saa tehdä mitä hyvänsä muuta sen lisäksi, mitä jälkiehto vaatii. Yleensä on vaikeaa tai mahdotonta määritellä formaalisti ja täydellisesti, mitä palvelin ei saa tehdä. Turvallisuusriski, varsinkin tuntematonta komponenttia käytettäessä! write -metodin jälkiehto (eri versioinaan) takaa kuitenkin sen, että tekstiin ei voi tulla muita muutoksia kuin halutun merkin lisäys. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 7

8 Korvattavuus (jatkoa) Myös perinteinen aliohjelma- tai luokkakirjasto on palveluntarjoaja, jolla on määritellyt rajapinnat ja joka palvelee useita asiakkaita. Jokaisessa kirjastoa käyttävässä järjestelmässä on kuitenkin vain yksi kirjaston toteutus, ja ainoastaan sen versionmuutokset voivat aiheuttaa asiakkaille uusia ongelmia. Komponenttipohjaisissa järjestelmissä samanlaisen (saman sopimuksen mukaisen) palvelun tarjoajia voi olla useita, ja ne voivat vaihtua dynaamisesti. Palveluntarjoajan sanotaan oleva korvattavissa jollakin toisella palveluntarjoajalla, jos jälkimmäinen toteuttaa (ainakin) saman sopimuksen kuin edellinen. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 8

9 Tyypeistä ja tyypintarkistuksesta Olisi ihanteellista, että sopimuksen kaikki ehdot voitaisiin esittää eksplisiittisesti ja formaalisti rajapinnan määritelmässä jokaisesta rajapintaa käyttävästä palvelimesta ja asiakkaasta voitaisiin tarkistaa automaattisesti, toteuttaako se ehdot. Edellinen tavoite on hyvin vaikea ja yleensä ainakin liian kalliiksi katsottu, jotta siihen päästäisiin. Jälkimmäinen tavoite ei yleensä ole mahdollinen silloinkaan, kun määritelmä on täysin formaali. Kannattaa kuitenkin tarkistaa niin paljon, kuin voidaan: suorituksen aikana mieluummin latauksen (tms. käyttöönoton) aikana mieluiten käännösaikana. Rajapinta määrittelee mm. operaatioiden parametrien ja tulosten tyypit, ja tämä sopimuksen aspekti on muita helpommin tarkistettavissa. Tietotyyppien määrittely ja käsittely on hyvin hallittu ala. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 9

10 Tyypeistä ja tyypintarkistuksesta (jatkoa) Tyyppivirheellä tarkoitetaan sitä, että jotakin arvoa tai muuttujaa käsitellään erityyppisenä, kuin se todella on. Szyperski käyttää termiä ”muistivirhe” (memory error). Laitteiston perustietotyyppien tasolla voi aiheuttaa hyvin hankalia ja vaikeasti selvitettäviä häiriöitä. Virhe voi koskea myös luokkaa tai muuta johdettua tyyppiä. Vahvasti tyypitetyiksi sanotaan ohjelmointikieliä, joissa tyyppivirheitä ei voi tapahtua. Useimmiten (esim. Javassa ja C#:ssa) virheet havaitaan ja estetään jo käännös- tai latausvaiheessa: staattinen tyypitys. Joissakin kielissä (esim. Smalltalkissa) tarkistus on suoritusaikainen. Monet staattisesti tyypitetyt kielet eivät ole vahvasti tyypitettyjä: esim. C ja C++ erityisesti osoitinaritmetiikkansa takia. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 10

11 Alityypit Jokainen rajapinta määrittelee myös tyypin, mutta esi- ja jälkiehdot eivät kuulu tyyppiin. Rajapinnan tyypille voidaan määritellä alityyppejä (alirajapintoja) lisäämällä siihen uusia operaatioita, esim.: interface View { void close (); void restore (int left, int top, int right, int bottom); } interface TextView extends View { int caretPos (); void setCaretPos (int pos); } interface GraphicsView extends View { int cursorX (); int cursory (); void setCursorXY (int x, int y); } Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 11

12 Alityypit (jatkoa) Jokin olio (ja siis sen luokka) voi toteuttaa yhden tai useita rajapintoja. Tyyppien näkökulmasta sillä voi siis olla useita tyyppejä. Alirajapinnan toteuttava olio toteuttaa aina myös ylirajapinnan. Olio on korvattavissa toisella oliolla tietyssä yhteydessä, jos jälkimmäinen toteuttaa edelliseltä tässä yhteydessä vaaditun rajapinnan. Rajapintaperintä (interface inheritance) Tämä ei tietenkään takaa korvattavuutta koko sopimuksen kannalta, mutta on silti hyödyllistä. Tähän perustuen oliokielten muuttujat ovat enimmäkseen polymorfisia: Ylityypin muuttuja voi viitata myös alityypin olioon. Erotukseksi muista polymorfismin lajeista puhutaan alityyppi- tai sisältymispolymorfismista (subtype / inclusion polymorphism). Alityypin olioiden joukkoa voidaan pitää ylityypin olioiden joukon osajoukkona. Joissakin kielissä noudatetaan ns. rakenteellista alityypitystä (structural subtyping). Jokin tyyppi on toisen alityyppi, jos edellinen sisältää ainakin kaikki jälkimmäisen operaatiot (kutsumuotoineen). Parempi nimitys olisi ”implisiittinen alityypitys”. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 17. 2. 2010 12

13 Alityypit (jatkoa) Myös operaation parametrien ja palautusarvon tyypit voidaan katsoa esi- ja jälkiehdon osiksi. Esim. Javassa kaikki parametrit ovat syöteparametrejä (in). Joissakin kielissä (mm. Adassa) on myös tulosparametrejä (out), jotka toimivat samoin kuin palautusarvo. Monissa kielissä on syöte-tulos-parametrejä (in out), joiden kautta tietoa voidaan välittää kumpaankin suuntaan. Syöteparametrien tyypit kuuluvat esiehtoon, tulosparametrien ja palautusarvon jälkiehtoon, syöte-tulos-parametrien molempiin. Staattisesti tyypittämättömissä kielissä (esim. Smalltalkissa) nämä täytyisikin määritellä esi- ja jälkiehdoissa. Tämän perusteella jotkin tyyppien uudelleenmäärittelyt alityypin operaatioissa ovat turvallisia. Palautusarvon ja tulosparametrin tyyppi voidaan määritellä alkuperäisen alityypiksi (kovariantti). Syöteparametrin tyyppi voidaan määritellä alkuperäisen ylityypiksi (kontravariantti). Syöte-tulos-parametrin tyyppiä ei voida muuttaa (invariantti). Luokan atribuutit vastaavat syöte-tulos-parametrejä, koska jokainen metodi voi sekä lukea että muuttaa niitä. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 25. 2. 2010 13

14 Alityypit (jatkoa) Täydennetään edellistä esimerkkiä käyttäen kovarianttia uudelleenmäärittelyä: interface View {... Model getModel (); } interface TextView extends View {... TextModel getModel (); } interface GraphicsView extends View {... GraphicModel getModel (); } Tämä on ilmeisen järkevää ja hyödyllistä. Jostain syystä Java ja useimmat muutkaan nykyiset kielet eivät kuitenkaan salli sitä. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 18. 2. 2010 14

15 Alityypit (jatkoa) Entä sitten käänteinen operaatio: interface View {... void setModel (Model m); } Selvästi tämän operaation parametrin tyyppi pitäisi määritellä uudelleen alirajapinnoissa samoin kuin getModel- operaation palautusarvon tyyppi. Rajapinnan toteuttavissa luokissa sama koskee atribuuttia, joka viittaa malliin. Mutta syöteparametrin (ja atribuutin) tyypin kovariantti uudelleenmäärittely ei ole turvallista. Esim. Eiffelissä se sallitaan, vaikka voikin aiheuttaa ajonaikaisen tyyppivirheen (ja siten poikkeuksen). Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 18. 2. 2010 15

16 Alityypit (jatkoa) Ratkaisuja, joilla tyyppivirheet voidaan havaita jo käännösaikana, vaikka kovariantti uudelleenmäärittely sallitaan: Eiffelissä ensin koko ohjelmiston laajuinen tyypintarkistus. Raskas, eikä sovellu itsenäisesti käytettäville komponenteille. Komponenttipohjaisessa ohjelmistossa kaikkien tarkistusten pitää olla modulaarisia. Tällaisen operaation kutsun salliminen vain monomorfisen muuttujan kautta. Eiffeliin on lisätty tällaisten muuttujien mahdollisuus. Vaatii myös sen, että operaation alkuperäisessä esittelyssä on ilmoitettava kovariantin uudelleenmäärittelyn mahdollisuus. Voidaan sanoa, että korvattavuus ei koske tällaisia operaatioita. Komponenttipohjainen ohjelmistotekniikka (Markku Sakkinen) – Osa 4 18. 2. 2010 16


Lataa ppt "Komponenttipohjainen ohjelmistotekniikka (TJTSS56) Osa 4 Kevätlukukausi 2010 Jyväskylän yliopisto Tietojenkäsittelytieteiden laitos Markku Sakkinen."

Samankaltaiset esitykset


Iklan oleh Google