Luento 4 T-106.1240 Ohjelmoinnin jatkokurssi T1 & T-106.1243 Ohjelmoinnin jatkokurssi L1 Luennoitsija: Otto Seppälä Kurssin WWW: http://www.cs.hut.fi/opinnot/t-106.1240/s2007
Projektiaiheista Jos et ole vielä valinnut aihetta, tee se nyt. Kaikkia aiheita on vielä tarjolla, mutta kahdessa aiheessa paikat loppuvat pian.
Yleissuunnitelma Yleissuunnitelma palautetaan Web-CATiin Palautuspaikka on kuin tavallinen tehtävä Suunnitelma tehdään HTML-sivuksi (saa aikaan monella selaimella, kirjoittamalla käsin tai vaikka tekstinkäsittelyohjelmalla) Suunnitelma pakataan JAR-ohjelmalla JAR löytyy jokaiselta koneelta, jolla voi kääntää java-koodia (tulee javan mukana) jar cvf paketin_nimi.jar suunnitelma.html kuva.gif... C = compress, V = verbose, F = filename Vastaavasti paketin voi purkaa komennolla jar xvf paketin_nimi.jar...tai millä tahansa zip-pakkausohjelmalla
Harjoituksista Kakkoskierroksen tehtävät 1 ja 3 ovat jo auki Kolmostehtävän ja kakkostehtävän deadlinet siirrettiin suunnitelmademoviikon yli. Kakkostehtävän tehtävänantoa selkiytetään. Ykköskierroksen deadline (Rästiaika alkaa) Ykköskierrokselta voi vielä kerätä pisteitä, mutta varsinaisen deadlinen jälkeen tehtävistä saatavat pisteet laskevat 20p päivässä seuraavan viiden päivän ajan. Tämän jälkeen kierros sulkeutuu lopullisesti.
Design
Design Mistä tiedetään, mitä luokkia pitäisi laatia tietyn ongelman ratkaisemiseksi? Millaisia kenttiä ja metodeita niille tulee? Ei ole yhtä parasta ratkaisua. Suunnittelutavoitteet voivat olla ristiriitaisia tasapainottelu, kompromissit. Ei ole menetelmää, jolla päästään varmasti hyvään tulokseen. Ei ole yleispätevää tapaa selvittää, onko jokin ratkaisu hyvä tai huono. Useimmille nyrkkisäännöille löytyy tapauksia, joissa kannattaakin toimia toisin.
Substantiivimenetelmä Substantiivimenetelmä on alkeismenetelmä, jota voi käyttää vaatimusmäärittelyn läpikäymiseen Kirjoita ohjelman toiminnasta sanallinen kuvaus ilman käyttöliittymään liittyviä asioita Kerää tekstistä substantiivit ja yritä ryhmitellä niistä ohjelman luokkia ja kenttiä. Kaikille substantiiveille ei ole käyttöä joten karsi ne aluksi pois Ne asiat joille voi laatia omia hyödyllisiä kenttiä ovat potentiaalisia luokkia Muut ovat usein luokkien kenttiä jotka voidaan monesti kuvata javan primitiivityyppejä ja merkkijonoja käyttäen Myöhemmin voi osoittautua, että on tarpeen tai hyödyksi laatia teknisiä apuluokkia, joilla ei ole suoraa vastinetta reaalimaailmassa. Liikkeelle lähdetään luokkarakennetta laadittaessa kuitenkin juuri aihealueen käsitteistöstä.
Substantiivimenetelmä Sijoita tämän jälkeen metodit sinne missä niiden käsittelemä data on Pohdi mihin arvoihin eri toimintojen ohjelmassa tulisi vaikuttaa Luokkien dataa ei pitäisi päästä käsittelemään kuin metodien kautta Tästä alkuasetelmasta voikin vaikka piirtää UMLdiagrammin ja lähteä suunnittelemaan tarkemmin. Suhtaudu substantiivimenetelmään varauksella Siitä voi olla alussa apua, mutta älä noudata sitä sokeasti.
Top-Down & Bottom-Up
Top-Down Top-down toteutuksessa ja suunnittelussa aloitetaan koko järjestelmän suunnittelusta Tämä määrittely on hyvin suurpiirteinen Määrittely jakaa järjestelmän alijärjestelmiin ja kertoo järjestelmän tasolla kuinka se käyttää alijärjestelmiä joiden kuvitellaan olevan olemassa Yllämainittu toistetaan alijärjestelmän tasolla Alijärjestelmä kuvataan käyttäen apuna pienempiä osasia Lopulta päästään alimmalle tasolle Yksittäiset luokat, metodit Todennäköisesti suunnitelmaa pitää tarkentaa useaan kertaan ennenkuin se on toteutettavissa
Top-Down Suunnitellessa määritellään järjestelmien ulkoiset rajapinnat ennen niiden toteutusta Toteutettaessa koodi rakennetaan käyttämään osia joita ei vielä ole olemassa Voidaan kirjoittaa tynkiä (oikeiden luokkien tilalla olevia luokkia, jotka jollakin tavalla simuloivat niitä) jotta päästään testaamaan...ja kääntämään Kirjoita ensimmäinen versio pseudokoodilla Älä kiinnitä alussa lainkaan huomiota syntaksiin tms. Kun runko on kasassa on suunnittelua helppo tarkentaa
Bottom-up Bottom-up strategia taas lähtee liikkeelle yksittäisistä osasista ja yhdistää niitä kierros kerrallaan suuremmiksi kokonaisuuksiksi Ohjelmointi ja testaus voidaan aloittaa heti Osasten yhdistely myöhemmin voi olla hankalaa koska ne on suunniteltu itsenäisesti Bottom-up tukee koodin uudelleenkäyttöä Vanhat osat saadaan sellaisenaan käyttöön Top-down suunnittelussa syntyvät rajapinnat eivät välttämättä sovi sellaisenaan vanhalle koodille
Top-Down + Bottom-Up Design Top-Down, Implement Bottom-Up Paras lopputulos saadaan yhdistämällä Top-down suunnittelulla vältetään moduulien yhteensopivuusongelmia ja saadaan kokonaiskuva projektista Yhdistettynä Bottom-up suunnitteluun koodin uudelleenkäyttö helpottuu Bottom-up toteutuksella päästään (yksikkö)testaamaan koodin osaset heti niiden valmistuessa
Design Suunnittelussa ohjelma kannattaa jakaa jonkinlaisiin alijärjestelmiin Tarpeettomia riippuvuuksia näiden välillä vältettävä Sykliset riippuvuudet voivat aiheuttaa ongelmia Esimerkki : Käyttöliittymän ja logiikan erottaminen GUI ohjaa, pyytää tietoa Aivot Logiikka (pelin logiikka, numeroilla tehtävä simulointi, tietokanta jne) kirjoitetaan itsenäiseksi osaksi joka ei tiedä millaisella käyttöliittymällä sitä käytetään. Pohdi millaisia metodeja luokkien julkiseen rajapintaan tarvitaan että eri käyttöliittymät voivat ohjata logiikkaa ja hekea tietoa Käyttöliittymä (GUI) on oma osionsa. Käyttäjän toimet kutsuvat GUI:n kautta logiikan metodeita. Käyttöliittymä hakee logiikalta ruudulla näyttämänsä tavaran.
Esimerkki Ristinolla-peli Logiikka GUI Pelilauta.java (Kuvaa pelilautaa: merkintöjen lisäys, voittotilan havaitseminen jne) Pelaaja.java (Rajapinta: metodi jolla kerrotaan että nyt on pelaajan vuoro, sekä joitakin joilla päivitetään pelaajan tietoja) Katsoja.java (Rajapinta: mm. metodi jolla kerrotaan että pelilauta päivittyi) TietokonePelaaja (täyttää rajapinnan Pelaaja) Joitakin graafisia elementtejä jotka piirtävät pelilaudan Pelilautaluokalta pyytämänsä datan perusteella. Jokin luokka täyttää rajapinnan Katsoja. Tämä päivittää graafisen näkymän. Vastaavasti jokin luokka täyttää rajapinnan Pelaaja.
Design Model-View-Controller Tämä suunnittelumalli menee vielä askeleen pidemmälle, erottaen GUI:n osat(controller) jotka muuttavat mallia(model) ja osat(view) jotka vain esittävät mallin sisältämää tietoa Controller Tiedot päivittyivät ohjaus muutoskäskyt View Esitettävän datan kysely Tiedot päivittyivät, käy kysymässä Model
Design Turhien riippuvuuksien välttäminen kannattaa myös alemmilla tasoilla Toisaalta samasta tiedosta ei yleensä kannata pitää useita kopioita Yhtä päivitettäessä pitää aina päivittää muutkin Myös riippuvasta datasta olevat kopiot voivat olla ongelmallisia Tällöin riippuvan datan turha uudelleenlaskeminen voi olla hyödyllistä.
UML
UML Olio-ohjelman luokkien pääpiirteet voidaan kätevähkösti esittää ns. UML-luokkakaaviona. Näin usein tehdäänkin esim. suunniteltaessa, millaisia luokkia ohjelmaan on tarkoitus laatia, ennen ohjelmakoodin kirjoittamista. UML, Unified Modeling Language, on kokoelma erilaisia ohjelmistojen tuottamisprosesseihin liittyviä merkintätapoja. Tämä luokkakaavionotaatio on yksi yleisimmin käytetyistä UML:n osista. Kokeile piirtää luokkakaavio tekniseen suunnitelmaasi Kun olet piirtänyt kaavion, pohdi kuinka jotkin ohjelmasi ominaisuudet toimivat: Kuinka luokkien metodit kutsuvat toisiaan kun jotain ominaisuutta käytetään. Onko kaaviossa kaikki tarvittu?
UML-mallinnuskieli UML UML-kieli luotiin 90-luvun puolivälissä korvaamaan joukko erilaisia mallinnuskieliä, joilla oli paljon yhteistä mutta myös paljon pikku eroja. Mallinnuskielen avulla on helppo suunnitella suurempia järjestelmiä, esittää tietoa nopeasti ja pienessä tilassa, selittää asioita muillekin kuin ohjelmoijille jne. jne. Parhaan kuvan mallinnuskielestä saa kokeilemalla sitä itse joten pitemmittä puheitta... Tutustutaan luokkadiagrammiin
UML luokkadiagrammi Nelio public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; public Nelio(Viiva yl,... this.ylalaita = yl; this.alalaita = al; this.vasenlaita = vl; this.oikealaita = ol; } public int laskepintaala(){... } public void asetavari(color c){...
UML luokkadiagrammi Nelio +laskepintaala() : int +asetavari(c:color):void public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; public Nelio(Viiva yl,... this.ylalaita = yl; this.alalaita = al; this.vasenlaita = vl; this.oikealaita = ol; } public int laskepintaala(){... } public void asetavari(color c){...
UML luokkadiagrammi -ylalaita -alalaita -vasenlaita -oikealaita Nelio :Viiva :Viiva :Viiva :Viiva +laskepintaala() : int +asetavari(c:color):void public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; public Nelio(Viiva yl,... this.ylalaita = yl; this.alalaita = al; this.vasenlaita = vl; this.oikealaita = ol; } public int laskepintaala(){... } public void asetavari(color c){...
UML luokkadiagrammi -ylalaita -alalaita -vasenlaita -oikealaita Nelio :Viiva :Viiva :Viiva :Viiva +laskepintaala() : int +asetavari(c:color):void näkyvyysmääreet + public - private # protected default public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; public Nelio(Viiva yl,... this.ylalaita = yl; this.alalaita = al; this.vasenlaita = vl; this.oikealaita = ol; } public int laskepintaala(){... } muuttujan, parametrin tai paluuarvon tyyppi kaksoispisteen jälkeen public void asetavari(color c){...
Assosiaatio Nelio Viiva public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; } public Nelio(Viiva yl, Viiva al, Viiva vl, Viiva ol){ this.ylalaita = yl; this.alalaita = al; this.vasenlaita = vl; this.oikealaita = ol; }
Assosiaatio Nelio 1..2 4 Viiva public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; } 3 tarkalleen kolme kpl public Nelio(Viiva yl, Viiva al, Viiva 0..5 vl, nollasta Viiva viiteen ol){ kpl this.ylalaita = yl; this.alalaita = al; this.vasenlaita = vl; this.oikealaita = ol; 2..* * vähintään kaksi kpl Vapaavalintainen määrä } Multiplicity (kerrannaisuus) Kertoo kuinka moneen toisen luokan olioon jokin luokan olio on yhteydessä Esimerkissämme yhteen neliöön kuuluu aina tasan 4 viivaa, mutta yksi viiva voi kuulua joko yhteen tai kahteen neliöön
Assosiaatio Nelio reunat 4 Viiva public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; } Rooli Karkeasti: kertoo mikä tämän suhteen pään merkitys on toiselle päälle Viivan merkitys neliölle on, että se public Nelio(Viiva yl, Viiva al, Viiva toimii vl, neliön Viiva reunana. ol){ this.ylalaita = yl; this.alalaita = al; this.vasenlaita = vl; this.oikealaita = ol; }
Navigability Nelio reunat 4 Viiva public class Nelio{ private Viiva ylalaita; private Viiva alalaita; private Viiva vasenlaita; private Viiva oikealaita; Viivan kentissä ei välttämättä ole viittauksia } public Nelio(Viiva yl, Viiva al, Viiva vl, Viiva ol){ this.ylalaita = yl; Neliö-luokan this.alalaita olioihin = al; this.vasenlaita = vl; this.oikealaita = ol; } Neliön kentissä on jollain tavalla tallessa viittauksia Viiva-olioihin
Kompositio ja aggregaatio kuvaavat, että jokin koostuu pienemmistä osasista. Jos pääolioon kohdistetaan operaatioita, ne kohdistuvat jollain tavoin myös sen osasiin. Kompositio on aggregaatiota jyrkempi kooste Kompositio Nelio reunat 4 Viiva Aggregaatio Neliö koostuu viivoista, jos neliö poistetaan viivatkin häviävät Pari 2 Yksilo Pari koostuu yksilöistä, jos pari purkautuu, yksilöt säilyvät silti
Riippuvuus Nelio reunat 4 Viiva +laskepintaala() : int +asetavari(c:color):void Riippuvuus Karkeasti: Luokka käyttää toisen luokan olioita tai niiden metodeita (esim. parametreina) mutta ei säilytä viittauksia sen olioihin Color
Perintä Kuvio Perintä Luokka perii toisen luokan ominaisuudet, ja sen olioon voidaan viitata yläluokan tyyppisen muuttujan kautta. Nelio reunat 4 Viiva
Esimerkki 2 vanhemmat Ihminen lapset * Rekursiivinen assosiaatio Luokan oliot voivat tietenkin viitata toisiin samanluokan olioihin. Esim Ihminen-luokassa voi olla viittaus muihin Ihminen-luokan olioihin.
Esimerkki Pankki Monta assosiaatiota Kahden luokan olioiden välillä saa olla myös useampia assosiaatioita, mutta tällöin kannattaa selittää mitä eri suhteet kuvaavat. 1 * Asiakas 1 omistaja * Pankkitili * * käyttöoikeus
Rajapinta <<interface>> Kuski Ajettava Auto Skootteri Rajapinta Rajapinta on kuin luokka, joka ei määritä yhdenkään metodin sisältöä, mutta sisältää vaatimuksen siitä, että tietynniminen ja kaltainen metodi on oltava olemassa. Rajapinta määrittää siis jonkinlaisen kyvyn, jonka sen täyttävät luokat osaavat. Rajapinnan käyttäjän ei tarvitse huolehtia rajapinnan täyttävän luokan tyypistä jos se käyttää vain rajapinnan tarjoamia metodeja
Rajapinnan täyttäminen <<interface>> Kuski Ajettava Rajapinnan täyttäminen Jos jokin luokka sisältää rajapinnan vaatiman listan metodit, se täyttää rajapinnan. Tätä suhdetta kuvataan nuolella jolla on ontto kärki ja katkoviivasta koostuva varsi Auto Skootteri
Abstrakti luokka <<abstract>> Työntekijä Abstrakti luokka Abstrakti luokka on tavallisen luokan ja rajapinnan välimuoto. Se on luokka, joka toteuttaa joitakin metodeita, mutta osan metodeista kohdalla se ainoastaan toteaa että ne täytyy jonkun muun luokan toteuttaa. Abstrakti luokka on puolivalmis luokka, joka antaa vapaat kädet puuttuvien metodien toteutukselle. Kokki Poliisi
Rajapinta Kuski Auto Ajettava
Design Patterns Suunnittelumallit
Suunnittelumalli Mikä on suunnittelumalli (Design Pattern) Suunnittelumalli on tiiviisti esitetty yleinen kuvaus jostakin toimivasta ratkaisusta yleiseen ongelmaan. Suunnittelumallien ideoita voi käytettäessä soveltaa vapaasti. Malleja voi myös yhdistellä, ja niin usein tehdäänkin. Vertailukohtana voi ajatella vaikkapa erilaisia puuliitostapoja, tanssiliikkeitä, Niksi-pirkan vinkkejä jne. Suunnittelumalli kuvaa yleisen ratkaisutavan ja sovellusalue lopulta määrää sen kuinka mallia kulloinkin käytetään.
Gang of Four Patterns Seuraavaksi käydään läpi joitakin kirjassa Gamma, Helm, Johnson, Vlissides : Design Patterns Elements of Reusable Object-Oriented Software, 1995 esiteltyjä suunnittelumalleja. Tässä kirjassa esitettyjä malleja kutsutaan yleisesti nimellä Gangof-Four patterns kirjan neljän kirjoittajan mukaan. Seuraavat kalvot perustuvat k.o. kirjaan.
Adapter Ongelma : Jokin osa olemassaolevaa koodia toimii tietyn rajapinnan täyttävien olioiden kanssa. Tarjolla olevien olioiden luokka ei kuitenkaan täytä tätä rajapintaa. Alkuperäistä luokkaa ei kuitenkaan haluta muuttaa. Ratkaisu : Adapter sovittaa jonkin olemassaolevan luokan rajapinnan muotoon, jota jokin toinen osa ohjelmasta odottaa. Tästä suunnittelumallista on kaksi versiota : olioadapteri luokka-adapteri
Adapter - olioadapteri Toteutus : Käyttävä luokka tekee kaiken toimintansa adapteri-olioiden kanssa, jotka täyttävät vaaditun rajapinnan. Adapterioliosta on viittaus varsinaiseen olioon Rajapinnan mukaiset metodikutsut delegoidaan viittauksen kautta eteenpäin oikealle oliolle Käyttävä Luokka Tuttu Rajapinta Adapteri Käytettävä Luokka
Adapter - luokkadapteri Adapteri voi myös periä luokan (jos mahdollista), jonka rajapintaa se muuttaa. Tällöin adapteri täyttää rajapinnan vaatimat metodit, joiden tehtävä on kutsua yliluokan metodeja. Käyttävä Luokka Tuttu Rajapinta Adapteri Käytettävä Luokka
Adapteri Esimerkki: List-olion joka sisältää Comparable rajapinnan täyttäviä olioita saa automaattisesti järjestettyä Collections-luokasta löytyvällä sort-metodilla. Robotti-luokan oliot haluttaisiin järjestää painon mukaan, mutta luokka ei täytä Comparable-rajapintaa. Ratkaistaan ongelma sovitinluokalla : public class VertailuSovitin extends Robotti implements Comparable { } public int compareto(object other){ if (this.annapaino() < other.annapaino()) return -1; else if (this.annapaino() == other.annapaino()) return 0; return 1; }
Factory Method Tehdasmetodi on metodi, jota käytetään olion luontiin konstruktorin sijaan Tyypillisesti metodi on nimeltään newinstance Käytännössä metodi toimii kuten konstruktori, palauttaen viittauksen halutun tyyppiseen olioon Voidaan toteuttaa kutsumalla konstruktoria metodin sisällä Voidaan palauttaa jokin olemassaoleva halutun tyyppinen olio Voidaan tehdä temppuja oliolle ennen kuin se palautetaan
Factory Method Factory method mallia käytetään mm. kun: 1) metodia kutsuva luokka ei itse voi tietää minkä aliluokan olion se haluaa luoda Esimerkiksi halutaan lisätä tehtävän 1.1 BSTNode luokkaan uusia kenttiä tekemällä uusi luokka EnhancedBSTNode joka periytyy luokasta BSTNode. Insert metodi tekee kuitenkin automaattisesti BSTNode-olioita uuden luokkamme sijaan -> pitäisi kirjoittaa insert uudelleen... Jos BSTNodet luodaankin tehdasmetodilla konstruktorin sijaan, voidaan tehdasmetodi korvata aliluokassa -> ongelma ratkaistu! BSTNode-luokassa public BSTNode newinstance(){ return new BSTNode(); } EnhancedBSTNode-luokassa public BSTNode newinstance(){ return new EnhancedBSTNode(); }
Factory Method Factory method mallia käytetään mm. kun: 2)kun luotavat oliot halutaan parametrisoida ennalta kutsuvasta luokasta riippumatta esim. asetetaan käyttöliittymän nappuloille haluttu väri ja teksti automaattisesti. Asetukset tarvitsee säätää vain kerran. 3)kun olioita halutaan laskea, käytöstä poistettuja olioita kierrättää jne. Kierrätys - Konstruktorin kautta ei voi tarjota jo aiemmin luotua oliota -> tehdasmetodin kautta voi
Strategy Oletetaan että on olemassa joukko erilaisia tapoja ratkaista jokin laskennallinen ongelma Strategy-mallissa kirjoitetaan ensin ratkaisulle yleinen toteutus jossa vaihtoehtoiset kohdat on ulkoistettu muiden luokkien tehtäväksi Vaihtoehtoisille kohdille on kirjoitetaan rajapinta jonka kautta yleinen toteutus käyttää ulkoistettuja osia. voidaan toteuttaa erilaisia ratkaisutapoja jotka toteuttavat k.o. rajapinnan kautta jonkin osan ratkaisua, mutta ei tarvitse kirjoittaa koko koodia Nyt ratkaisutapaa voidaan haluttaessa helposti muuttaa esim. tekstin jakaminen riveihin vaihtelee kielen mukaan esim. alkioiden vertailu keskenään järjestettäessä
Strategy import java.util.*; public class StrategyExample{ public static void main(string[] args){ Arrays.sort(args, String.CASE_INSENSITIVE_ORDER); for (int i=0; i< args.length; i++) System.out.println(args[i]); } /* Vaihdetaan järjestämisstrategiaksi toisen * merkin mukaan järjestäminen */ Arrays.sort(args, new Comparator(){ public int compare(object one, Object other){ return ((String)one).charAt(1)- ((String)other).charAt(1); } } public boolean equals(object other){ return this.equals(other); } } ); System.out.println(); for (int i=0; i< args.length; i++) System.out.println(args[i]); Tässä esimerkissä järjestämisalgoritmi on kirjoitettu Arraysluokkaan. Ulkoistettu osa algoritmia on vertailu, jonka koodari voi asettaa antamalla haluamansa Comparator-olion. Esimerkissä luodaan Comparator joka järjestää merkkijonot niiden toisen merkin mukaan.
Template Method Template method toimii muuten kuin Strategy, mutta puuttuvat osat algoritmia on kirjoitettu abstrakteiksi metodeiksi Metodit toteutetaan aliluokassa jonka jälkeen algoritmi on käyttökelpoinen
Iterator Monesti jonkin tietorakenteen kaikki alkiot tulisi käydä läpi (vaikkapa tallentaa tiedostoon) Tietoa voi kuitenkin olla monenlaisissa rakenteissa: puita, listoja, verkkoja jne. Iterator-rajapinta mahdollistaa rakenteen läpikäynnin ilman että sitä käyttävän tarvitsee tuntea rakenne Koska Java API:ssa on valmis rajapinta tälle, kannattaa tätä mallia käyttävän aina käyttää mainittua rajapintaa.
Proxy Proxy toimii jonkin toisen olion sijaisena ja säätelee olioon kohdistuvaa käyttöä. Kun jotain proxy-olion metodia kutsutaan, se yleensä kutsuu todellisen olion metodia Tyypillisesti sekä proxy-olio että oikea olio täyttävät saman rajapinnan, joten käyttävä luokka ei huomaa eroa Käyttävä Luokka <<interface>> Kohde +operaatio() todkohde.operaatio() TodellinenKohde +operaatio() todkohde Proxy +operaatio()
Proxy Käyttötapoja : Proxyn kauttaa voidaan käyttää olioita jotka sijaitsevat aivan muualla (levyllä, verkossa jne.) (remote proxy) Lazy initialization: Proxyn suojaama olio voidaan alustaa vasta tarvittaessa. (virtual proxy) Proxyn avulla saman luokan eri olioille voidaan antaa erilaiset näkyvyydet. (protection proxy) Proxy mahdollistaa sen suojaaman olion vaihtamisen ilman päivityksiä siihen osoittavissa olioissa. Tietorakenne ei voi tietää kuka siihen viittaa, joten esimerkiksi rakenteen tyhjentyminen ja täyttyminen uudestaan voisi ilman proxya olla ongelma Asetusten vaihtaminen kesken ohjelman ajon jne...
Decorator Mahdollistaa lisäominaisuuksien lisäämisen olioon lennossa, muuttamatta alkuperäistä luokkaa. Perusidea on, että dekoraattori liittää johonkin alkuperäiseen toimintoon oman lisänsä ja pyytää sitten koristelemaansa oliota (oli se sitten dekoraattori tai varsinainen olio) suorittamaan alkuperäisen toiminnon. Lopulta varsinainenkin operaatio tulee tehtyä Koska sekä dekoraattorit että varsinainen koristeltava olio täyttävät saman rajapinnan, ovat dekoraattorit käyttävälle luokalle näkymättömiä. Reunustaja Värittäjä TodellinenOlio Käyttävä Luokka +operaatio() +omatoiminto() +operaatio() +omatoiminto() +operaatio()
Decorator Edellisen esimerkin UML-kaavio Käyttävä Luokka <<interface>> Komponentti koristettava TodellinenKomponentti +operaatio() +operaatio( ) <<abstract>> Dekoraattori +operaatio() +omatoiminto() operaatio(){ this.omatoiminto(); koristettava.operaatio(); } Koristelija Värittäjä Reunustaja
Composite Joissakin tietorakenteissa (esim. puu) rakenteen osaset koostuvat pienemmistä osasista (lapset), jotka taas koostuvat pienemmistä osasista jne. Composite-mallissa koko rakenteelle voi suorittaa operaation tekemällä sen vain yhdelle osaselle Toteutuksessa suurempaa rakennetta ja sen osasia käsitellään samalla tavalla. Kaikki osaset täyttävät saman rajapinnan. Isomman rakenteen operaatio tehdään myös sen lapsille
Composite Esimerkki Tekstinkäsittelyohjelma, jossa kappaleet koostuvat alikappaleista ja tekstistä, jotka molemmat täyttävät rajapinnan TekstiRakenne TekstiRakenne-luokassa on metodi oikolue() Kappale-luokan oikolue kutsuu metodia oikolue() sisältämilleen Kappale-olioille ja sisältämälleen Teksti-oliolle Teksti-olio suorittaa oikoluvun normaalisti. Etuja: Koko tekstin tai osan siitä voi oikolukea kutsumalla metodia oikolue vain kerran. Muun osan koodia ei tarvitse toimia eri tavalla riippuen siitä, millaista tekstirakennetta käsitellään, koska toiminta on ulospäin samanlaista.
Composite UML-kaavio Huomaa että toteutuksessa erilaisia Komponentti-rajapinnan täyttäviä rakenteita voi olla paljon erilaisia, tai jopa vain yksi Käyttävä Luokka <<interface>> Komponentti +operaatio( ) lapset Alkio +operaatio() Komposiitti +operaatio() Suorittaa kaikille lapsille metodin operaatio()
Command Joskus halutaan suorittaa jokin toiminto tietämättä ennalta mitään kutsuttavasta metodista tai oliosta jonka metodia kutsutaan. Command-mallin idea on kapsuloida metodikutsu olion sisään. Komento-olioita vaihtamalla voidaan vaihdella toiminnallisuutta Oliot voivat Command-olioiden kautta kutsua metodeita, jotka voidaan toteuttaa niistä täysin irrallisina. Komentoa Käyttävä Luokka Kohde +operaatio() komennonkohde komennonkohde.operaatio() <<interface>> Komento +suorita() TodellinenKomento +suorita()
Command Command-olioita voidaan puskuroida, tallentaa jne. Voidaan tehdä makroja, tai suorittaa vasta kun mahdollista Komennoista voidaan joissain tilanteissa tehdä peruutettavia. Suorita()-metodi tallentaa tällöin olennaiset tiedot jotta peruutuksen voi tehdä. Lisätään rajapintaan metodi peruuta() Laitetaan Komento-oliot talteen niiden suorituksen jälkeen PoistuKomento Ohjelma Komentoa Käyttävä Luokka <<interface>> Komento +suorita() +suorita() KopiointiKomento +suorita() +lopeta() Leikepöytä +kopioivalinta()
Patterns & AWT/Swing Observer-pattern Tapahtumankuuntelijat Composite-pattern Komponenttien piirtäminen Komponenttien asettelu ruudulle Abstract Factory Käyttöliittymän ulkoasun vaihtaminen
Observer Observer Observer mahdollistaa toiminnan, jossa muutos yhdessä oliossa aiheuttaa automaattisesti muutoksen kaikissa siitä riippuvissa olioissa Riippuvat oliot vain pyytävät kiinnostavaa oliota kertomaan muutoksista Käytännössä kiinnostavalla oliolla on lista johon kiinnostuneet (Listener, Observer) voivat ilmoittautua Kun jotain tapahtuu, käydään jokainen listallaolija läpi ja kutsutaan jokaisen kohdalla ilmoitusmetodia. Yleensä ilmoitusmetodille vielä annetaan pikku viesti (Event) joka kuvaa tapahtuneen asian Kuunneltavan ei tarvitse tietää kuuntelijasta muuta kuin että se täyttää kuuntelija -rajapinnan
Observer UML-kaavio <<interface>> Havainnoitava +lisaakuuntelija( ) +poistakuuntelija( ) +ilmoitamuutos( ) havainnoijat[] Suorittaa kaikille havainnoijille metodin ilmoitamuutos() <<interface>> Havainnoija +ilmoitamuutos() SeurattavaLuokka HToteutus voi halutessaan reagoida muutokseen HavainnoijaToteutus
Observer Observer-malli Yksi parhaista esimerkeistä Observer-mallin käytöstä on javan Swing- ja AWT-käyttöliittymien käyttämät tapahtumankuuntelijat Käytännössä mallia voidaan käyttää mihin tahansa tilanteeseen, jossa useamman paikan ohjelmassa tulee reagoida johonkin muutokseen. Tulostus päättyy Puuhun lisätään solmu Äänikanava suljetaan jne..
Observer import java.awt.*; import java.awt.event.*; public class Example extends Frame{ public static void main(string args[]){ new Example().start(); } public void start(){ final Button mybutton = new Button( Press Me ); ActionListener hello = new HelloListener(); mybutton.addactionlistener(hello); mybutton.addactionlistener(new ActionListener(){ public void actionperformed(actionevent ae){ mybutton.setlabel( Pressed! ); } }); add(mybutton); pack(); setvisible(true); } } public class HelloListener implements ActionListener{ public void actionperformed(actionevent ae){ System.out.println( Hello World! ); } }
Suunnittelumallit ja uudelleenkäyttö Adapter Mahdollistaa vanhan koodin käyttämisen. Toimii vanhan koodin uutena julkisivuna joka tarjoaa toisentyyppisen ulkoisen rajapinnan, mutta käyttää alla vanhan luokan metodeja Strategy Mahdollistaa saman algoritmikoodin uudelleenkäytön ulkoistamalla eri käyttötarkoituksissa vaihtuvat osat toisiin luokkiin Template Method Kuten yllä, mutta vaihtuvat osat korvataan aliluokissa
Uudelleenkäyttö Observer Luokan kirjoittajan ei tarvitse tietää minkä tyyppiset oliot reagoivat luokan olion tilan muutoksiin. Command Uudentyyppisiä komentoja voi lisätä juuri lainkaan muuttamatta koodia joka kutsuu näitä komentoja Composite Rakenteeseen voi lisätä uudentyyppisiä komponentteja mutta vanhoja luokkia ei tarvitse muuttaa koska kaikkea käytetään rajapintojen kautta Decorator Oliolle voi (lennossakin) lisätä uusia ominaisuuksia muuttamatta vanhaa luokkaa lainkaan
Uudelleenkäyttö Iterator Koodia joka käsittelee muita rakenteita Iteratoreilla voidaan uudelleenkäyttää vaikka rakenne muuttuisi puusta verkoksi tms. Proxy Ei ehkä niinkään uudelleenkäyttöä edistävä malli, mutta mainitsemisen arvoinen muista syistä. Factory Method Yläluokkien versioita algoritmeista ei tarvitse kirjoittaa uudelleen jos oliot luodaan tehdasmetodeilla konstruktorien sijaan (konstruktoria ei voi korvata, metodin voi)
Defensive Programming Mieti miten kirjoittamaasi metodia tai luokkaa voitaisiin käyttää väärin Todennäköisesti väärinkäyttö johtuu väärinymmärryksestä Koodin käyttäjä kutsuu metodeja A ja B väärässä järjestyksessä Käytetään vääräntyyppisiä parametreja jne...mutta omat ohjelmointivirheetkin saattavat hyvin kutsua metodeja laittomilla arvoilla... ja varaudu näihin tapauksiin Jos mahdollista, rakenna koodi niin että sitä ei voi väärinkäyttää tai tarkista metodien syötteet jne. Helpottaa virheiden löytämistä : Jos metodia kutsutaan väärillä arvoilla, niin virhe ei ilmaannu metodin sisällä -> osataan aloittaa kutsuvasta koodista
Defensive Programming Työkalut if-lauseet ja poikkeukset Jos metodilla on rajoitteita sen suhteen millaisia parametreja se voi ottaa vastaan, kannattaa ne tarkistaa ja tarvittaessa heittää poikkeus tai palauttaa jokin metodin dokumentaatiossa sovittu arvo. assertiot Javassa on Junit:in assertxxx komentoja vastaava oma assert()- metodi, joka toimii jotakuinkin vastaavalla tavalla. Paikkoihin, joissa muuttujien arvojen tulee ehdottomasti olla tietyissä rajoissa voi kirjoittaa assertioita, jotka katkaisevat ohjelman suorituksen virhetilanteessa. Nämä voi kytkeä pois käännettäessä testaus Kokeile käyttää luokkaasi väärin ja katso ettei se onnistu.
Defensive Programming Työkalut final Määreellä final voi estää metodin korvaamisen aliluokissa. Luokkaan sovellettuna final estää aliluokkien tekemisen. Määreellä voi suojata metodit tai luokat joiden korvaaminen aliluokilla voisi rikkoa niitä käyttävän luokan toiminnan, tms. private Suojaa private-määreellä apumetodit jotka on tarkoitettu luokan sisäiseen käyttöön. Vastaavasti luokan kenttien näkyvyys tulisi olla lähtökohtaisesti private josta poiketaan vain hyvillä syillä. koodin muokkaaminen
Tulevia luentoja (alustava) 8.10 Graafiset käyttöliittymät, Versionhallinta Tapahtumat ja tapahtumankäsittely, GUI:n perustoiminnallisuus, Grafiikan piirtäminen. Versionhallinta. 15.10 Rinnakkaisohjelmoinnin alkeet Javan rinnakkaisuusprimitiivit, Luokka Thread, Runnablerajapinta, Perus Producer-Consumer esimerkkitoteutus, blocking, potentiaaliset ongelmat kuten deadlock. Graafiset käyttöliittymät ja rinnakkaisuus 22.10 Vaihtelevia aiheita Esim. Ohjelmien profilointi ja profiilien tulkinta, asiaa koodin optimoinnista, Lisää kirjastoja, yleiskatsaus palautettuihin suunnitelmiin ja ohjeistusta projektiin.