582104 Ohjelmistojen mallintaminen, suunnittelumalleja 1
Suunnittelumallit (design patterns) Kuvaus sellaisesta luokkarakenteesta & olioiden vuorovaikutuksesta, joka ratkaisee tietyn yleisen ongelman tiettyjen reunaehtojen vallitessa Keskeinen lähde suunnittelumalleihin Gamma et al., Design Patterns Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995 2
Ongelma kontekstissaan Gamma & al. dokumentoivat suunnittelumallinsa dokumentoidaan standardoidussa formaatissa: 1. Nimi 2. Ongelmakuvaus 3. Ratkaisu 4. Seuraukset Mallit ovat sidottuja johonkin ympäristöön Ei-olioympäristössä voisivat oliomaaliman peruskäsitteet perintä, kapselointi ja polymorfismi olla omia suunnittelumallejaan 3
Mallien luokittelu Gamma et al. jaoittelevat mallinsa kolmeen pääkategoriaan 1. Olioiden luontimallit (creational) 2. Olioiden käyttäytymismallit (behavioral) 3. Luokkien rakenteelliset mallit (structural) Mallien tunteminen ja käyttäminen helpottaa kommunikaatiota suunnittelijoiden välillä Yhteinen sanasto Ohjelmakoodia korkeampi abstraktion taso 4
Luokkien ilmentymien luonti Kuka kutsuu luokan konstruktoria uuden ilmentymän luomiseksi? Koska ilmentymiä pitäisi luoda? jotkut oliot luodaan ohjelman käynnistyessä main- tai alustusoperaatiossa monet oliot luodaan dynaamisesti suoritusaikana, mutta niiden luoja on tiedossa staattisesti (käännösaikana) joissain tapauksissa olion luoja määräytyy dynaamisesti vasta suoritusaikana 5
Luontimalli new() Yksinkertaisin, ja kaikkien monimutkaisempien luontimallien hyödyntämä olion luontitapa on käyttää kielen new-avainsanaa: new Kirahvi(); new-avainsanaa käyttäessä kiinnitetään se konkreettinen luokka, josta olio halutaan instantioida Tämä vaikeuttaa geneeristen, uudelleenkäytettävien ohjelmien tekemistä Luontimalleja: abstrakti tehdas, rakentaja, tehdasmetodi, prototyyppi, ainokainen 6
Luontimalli tehdasmetodi (factory method) Määritellään luokkaan metodi, jonka tehtävä on luoda halutunlaisia olioita. Merkitys 1 (puhtaampi oliotapa) Aliluokka kiinnittää luotavan olion tyypin syrjäyttämällä yliluokan metodin Merkitys 2 (yleisempi) Metodi jollain keinoin päättää, minkä luokan olio instantioidaan 7
Esimerkki tehdasmetodista public static Frequency getfrequency(double d) { Frequency f; if(testing) { f = new MockFrequency(d); else { f = new ObservableFrequency(d); return f; 8
Luontimalli abstrakti tehdas Erilaisten, yhteenkuuluvien olioperheiden tuottamiseksi sopii luontimalli abstrakti tehdas Abstrakti rajapinta yhteenkuuluvien olioiden luomiseksi kiinnittämättä niiden konkreettisia luokkia Mahdollistaa sovelluksen konfiguroimisen luomaan tietyn konkreettisten aliluokkien perheen ilmentymiä 9
Abstrakti tehdas UML:nä Client AbstractFactory createproducta() createproductb() ConcreteProductA1 AbstractProductA ConcreteProductA2 ConcreteFactory2 ConcreteFactory1 AbstractProductB createproducta() createproductb() createproducta() createproductb() ConcreteProductB1 ConcreteProductB2 10
Olioiden käyttäytymismallit Olioiden ajonaikaisen käyttäytymisen mallintaminen Käyttäytymismalleilla tyypillisesti pyritään parantamaan järjestelmän joustavuutta mahdollistamalla käyttäytymisen parametrointi ja/tai yksityiskohtien piilottaminen Malleja esim. vastuuketju, iteraattori, hiutale, operaatiorunko, vierailija,.. (Chain of responsibility, Iterator, Flyweight, Template method, Visitor).. 11
Tapahtumakäsittely Synkroninen kommunikointi aiheuttaa operaatioriippuvuuksia moduulien välille Asynkronisessa kommunikoinnissa palvelun käyttäjä on löyhemmin sidottu palvelun tarjoajaan Eräs asynkronisen kommunikoinnin muoto on tapahtumaperustainen kommunikointi voidaan toteuttaa esimerkiksi Observersuunnittelumallin avulla [Gamma et al., 1995] Javassa valmis tuki rajapinnalla java.util.observer ja kantaluokalla java.util.observable 12
Tapahtumankäsittelyn toteutus Tapahtuman lähettäjä (event originator, publisher) luo tyypillisesti tapahtumaolion (event object), joka kuvaa tapahtumaan liittyvää informaatiota (esim. ButtonEvent: mikä painike) Tapahtuman käsittelijät (event listener, observer, subscriber) ovat aiemmin ilmoittautuneet halukkaiksi tulemaan informoiduksi kyseisenlaisista tapahtumista tapahtumaolio välitetään niille kaikille käsiteltäväksi sopivasti Laajoissa järjestelmissä voi olla erillinen rekisteröijä (registrator object), jolle tapahtuman käsittelijät ilmoittautuvat ja joka huolehtii lähettäjien ja käsittelijöiden välisestä kättelystä 13
Tapahtumankäsittelyn aiheuttamat riippuvuudet Tapahtuman lähettäjä ei tiedä (eikä haluakaan tietää), kuinka käsittelijä tulee tapahtumaan reagoimaan lähettäjä riippuu käsittelijästä vain hyvin löyhästi käytännössä se yleensä tuntee vain joukon tietyn kuuntelijarajapinnan toteuttavia olioita Käsittelijän ja lähettäjän välinen kättely aiheuttaa vahvemman riippuvuuden jos käytetään erillistä rekisteröijäoliota, se riippuu sekä tapahtuman lähettäjästä että käsittelijästä jos käsittelijä rekisteröityy suoraan lähettäjälle, se riippuu lähettäjästä rajapintojen käytöllä voidaan löyhentää riippuvuuksia 14
PDialogEvent PDialog +adddialoglistener() -firedialogevent() +dook() Kuuntelijarajapinnan käyttäminen tapahtumankäsittelyssä «layer» presentation «uses» PWindow +init() IPDialogListener +processdialogevent() «layer» control ICPresenter +init() CActioner CInit «uses» +processdialogevent() public class PDialog { private CActioner IPDialogListener act; lst; private PDialogEvent evt; public class CActioner { public void adddialoglistener(ipdialoglistener adddialoglistener(cactioner a) { l) act { lst = = a; l; public void processdialogevent( private void firedialogevent() { PDialogEvent evt) { if (evt!= null) evt = new PDlgEvent(); // do something with evt if (act (lst!= null) lst.processdialogevent(evt); act.processdialogevent(evt); 15/26 public void dook() { firedialogevent(); 15
Iteraattori Iteraattorilla erotellaan oliokokoelman sisäinen rakenne ja kokoelman läpikäymisen logiikka toisistaan Käyttö hyvin yksinkertaista: List<Radio> radios = new ArrayList<Radio>();. for(radio r : radios) { print ( r ); Iterointirajapinnan tarjoaminen ns. asiakaspalvelua 16
Operaatiorunko (template method) Määritellään operaatiolle yleinen runko, yksityiskohdat vaihtelevat olioverkon konfiguraation mukaan 17
Operaatiorunko checkfrequency() protected boolean checkfrequency(int frequency) { int nextstep = 0; while(nextstep < 1000) { if(!checkfrequency(frequency, nextstep)) { return false; nextstep += 25; return true; checkfrequency(int, int) antaa variaatiopisteen operaatiorungolle 18
Vastuuketju (chain of responsibility) Erotetaan palvelupyynnön lähettäjä palvelun toteuttajasta antamalla useammalle oliolle mahdollisuus huolehtia palvelun toteutuksesta Toteuttaja delegoi palvelupyynnön ketjussa seuraavalle, jos se ei itse pysty toteuttamaan palvelua (kokonaan) Client Handler handlerequest() successor ConcreteHandler1 handlerequest() ConcreteHandler2 handlerequest() 19
Radiotarkistuksen vastuketju public boolean check() { for(radio radio : radios) { if(!radio.check()) { return false; return true; 20
Rakennemallit Rakennemallit käsittelevät tapoja järjestää luokka- ja oliorakenteita Esim. sovitin (adapter), hiutale (flyweight), edustaja (proxy), silta (bridge), rekursiokooste (composition), 21
Sovitin Olemassaolevaa luokkarakennetta uudelleenkäytettäessä rajapinnat eivät aina ole yhteensopivia Sovitin-mallilla rajapintojen väliin sijoitetaan.. sovitin 22
Hiutale (flyweight) Hiutale-mallilla yhdistetään samanlaisia tai samankaltaisia olioita muistin säästämiseksi Esim. tekstinkäsittelyjärjestelmässä jokainen yksittäinen kirjain Sivuvaikutuksena samankaltaiset oliot voivat myös toimia kommunikaatioyhteyksinä 23
Frequency-flyweight public static Frequency getfrequency(double d) { Frequency f = frequencycache.get(d); if(f == null) { f = new MockFrequency(d); frequencycache.put(d, f); return f; 24
Yhteenvetona Nyt läpikäytiin vain muutamia yleisesti esiintyviä suunnittelumalleja, hyvin yleisellä tasolla Mallien käyttö on monesti eri mallien koostamista ja yhteensovittamista Esim. Flyweight + Factory method 25