2015 syksy 2. vsk VII Suunnittelumallit Adapter ja Composite
Sisältö 1. Johdanto rakennemalleihin 2. Adapter (Sovitin) 3. Composite (Rekursiokooste) Suunnittelumallit Adapter ja Composite 2
VII.1 Johdanto rakennemalleihin Pääasia: miten luokkia ja olioita yhdistellään muodostamaan laajempia rakenteita Rakenteelliset luokkamallit Perustuvat periytymiseen Rakenteelliset oliomallit Perustuvat olioiden yhdistelemiseen Adapter (Sovitin) ja Composite (Rekursiokooste) rakennemalleja Suunnittelumallit Adapter ja Composite 3
VII.2 Adapter (Sovitin) Yleinen ongelma: käytettävissä luokka jonka 1. Toiminta haluttu 2. Rajapinnan käyttö mahdotonta tai hankalaa 3. Muuttaminen ei mahdollista Ongelma erityisesti uudelleenkäyttötilanteissa Ratkaisu: Kirjoitetaan sovitinluokka vanhan luokan ja uuden ohjelmakoodin väliin Adapter-suunnittelumalli Malli yleinen Javan luokkakirjastoissa Suunnittelumallit Adapter ja Composite 4
VII.2.1 Adapter-esimerkki Javasta Käyttöliittymän ikkunaluokan ikkunatapahtumien käsittelijän toteutettava rajapinta WindowListener: public void windowopened(windowevent e) public void windowclosed(windowevent e) public void windowactivated(windowevent e) public void windowdeactivated(windowevent e) public void windowiconified(windowevent e) public void windowdeiconified(windowevent e) public void windowclosing(windowevent e) Kaikki metodit toteutettava Javassa luokka WindowAdapter, tyhjät toteutukset -> perimällä tarvitsee toteuttaa vain halutut metodit Suunnittelumallit Adapter ja Composite 5
VII.2.2 Adapter: Tarkoitus ja sovellettavuus Tarkoitus (Intent) Muuntaa luokan rajapinnan asiakkaiden vaatimaan muotoon ja mahdollistaa muuten rajapinnoiltaan yhteensopimattomien luokkien yhteistoiminnan Sovellettavuus (Applicability) Valmiin, rajapinnaltaan sopimattoman luokan käyttö Luo uudelleenkäytettävän luokan toimimaan toistaiseksi tuntemattomien luokkien kanssa. On käytettävä useaa olemassa olevaa aliluokkaa, mutta ei haluta periä kaikkia. Oliosovitin voi sovittaa yliluokkansa rajapinnan Suunnittelumallit Adapter ja Composite 6
VII.2.3 Adapter: Rakenne Luokkasovitin Sovitettava luokka peritään sovitinluokkaan Kommunikoi Targetin määräämän rajapinnan kautta Sovelluskohtainen rajapinta, jonka kautta kommunikoi Clientin kanssa Sovitettava rajapinta Sovittaa Adapteen rajapinnan Targetin määräämään rajapintaan Luokkasovittimen rakenne. Ks.: Gamma, Helm, Johnson, Vlissides: Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley 1995 7
VII.2.3 Adapter: Rakenne (2) Oliosovitin Sovitettavan luokan olio osaoliona sovittimessa Oliosovittimen rakenne. Ks.: Gamma, Helm, Johnson, Vlissides: Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley 1995 8
VII.2.4 Luokkasovitin vs. Oliosovitin Perustapauksessa voidaan valita kumpi vain Tapaus 1: Luokan Adaptee aliluokissa on määritelty operaatioita, jotka on myös sovitettava. Jos joskus suoritettava aliluokan metodi, on käytettävä oliosovitinta Perittäessä luokka Adapter perii vain Adaptee-luokan toteutuksen Tapaus 2: Adapter-luokan on uudelleenmääriteltävä joitakin Adaptee-luokan toimintoja -> Käytä luokkasovitinta Aliluokassa uudelleenmäärittely helpompi toteuttaa Suunnittelumallit Adapter ja Composite 9
VII.2.5 Esimerkki Ohjelmassa luokka, muokattava merkkijonotaulukkoa class StringContainer { private: std::string* stringtable; int length; public: StringContainer(std::string* intable, int len); // Taulukon muokkaus- // metodi std::string* getmodifiedstrings() const; }; Valmis luokka, muokkaa vectoria class StringVectorModifier { public: // Metodi palauttaa // muokatun vectorin void modify( std::vector<std::string> &list); }; Suunnittelumallit Adapter ja Composite 10
VII.2.5 Esimerkki (2) Sovittimeen Target-luokka (abstrakti) class StringArrayModifier { public: }; virtual std::string* modifyarray(std::string *array, int length)=0; Adapter-luokan määrittely class VectorArrayAdapter: private StringVectorModifier, public StringArrayModifier { public: }; std::string* modifyarray(std::string* array, int length); Suunnittelumallit Adapter ja Composite 11
VII.2.5 Esimerkki (3) Adapter-luokan metodin toteutus std::string* VectorArrayAdapter::modifyArray(std::string* array, int length){ // Määrittele string-olioita sisältävä vector strings // Kirjoita taulukon array sisältämät merkkijonot // vectoriin strings // Muunna vectorin strings sisältö modify(strings); } // Luo uusi taulukko modified_table, kirjoita // vectorin strings merkkijonot siihen // ja palauta taulukko return modified_table; Suunnittelumallit Adapter ja Composite 12
VII.2.5 Esimerkki (4) Lopuksi muutos Clientia vastaavaan luokkaan: std::string* StringContainer::getModifiedStrings() const { VectorArrayAdapter va; std::string *modifiedstrings = va.modifyarray(stringtable, length); } return modifiedstrings; Suunnittelumallit Adapter ja Composite 13
VII.3 Composite Lähtöisin graafisten käyttöliittymäkomponenttien rakentamisesta Käsitellään komponenttien muodostamaa koostetta käyttöliittymässä yksittäisenä komponenttina Yleistys: Muodostetaan olioiden ryhmiä, joita halutaan käsitellä kuin yksittäistä oliota -> Composite-malli Luokkahierarkian kantaluokasta periytyy kahdenlaisia luokkia Luokat, jotka määrittelevät alkeisolioita (ei jälkeläisiä) Luokat, jotka määrittelevät yhdisteolioita: koostavat alkeisolioita monimutkaisemmiksi yhdistelmiksi Suunnittelumallit Adapter ja Composite 14
VII.3 Composite (2) Tarkoitus Järjestää oliot puuhierarkioiksi, jotka kuvaavat osakokonaisuus-suhteita Mahdollistaa niin osien kuin kokonaisuuksien yhdenmukaisen käsittelyn Sovellettavuus Mallintaa osa-kokonaisuussuhteita Asiakasolioiden voitava käsitellä sekä osia että kokonaisuuksia samalla tavalla Suunnittelumallit Adapter ja Composite 15
VII.3.1 Composite: Rakenne Käsittelee koostetta Componentin määräämän rajapinnan kautta Kaikkien komponenttien kantaluokka Primitiivisten olioiden luokka Yhdisteolioiden luokka Composite-mallin luokkarakenne. Lähde: Gamma, Helm, Johnson, Vlissides: Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley 1995 16
VII.3.1 Composite: Rakenne (2) Composite-mallin tyypillinen oliorakenne. Suunnittelumallit Adapter ja Composite 17
VII.3.4 Composite: Etuja ja haittoja Etuja Yhdistelmäolioita ja primitiivisiä olioita käsitellään yhtenäisesti -> asiakasluokan koodi yksinkertaista ja uusien komponenttien lisääminen helppoa Asiakkaan ei tarvitse tietää minkälaista oliota käsittelee Haittoja Mallin yleisyydestä ongelmia: Komponentin sisältämien olioiden tyyppien rajoittaminen vain ajonaikaisilla tyypintarkistuksilla Suunnittelumallit Adapter ja Composite 18
VII.3.5 Composite: Esimerkki // Abstrakti kantaluokka class Component { public: virtual void operation()=0; virtual void add(component *component)=0; virtual void remove(component *component)=0; virtual ~Component(){} }; // Primitiivisten olioiden luokka class Leaf: public Component { public: void operation(); void add(component *component); void remove(component *component); }; Suunnittelumallit Adapter ja Composite 19
VII.3.5 Composite: Esimerkki (2) void Leaf::operation() { std::cout << "Leaf address: " << this << std::endl; } void Leaf::add(Component *component) { throw std::runtime_error("no adding in leaf"); } void Leaf::remove(Component *component){ throw std::runtime_error("no removing from leaf"); } Suunnittelumallit Adapter ja Composite 20
VII.3.5 Composite: Esimerkki (3) // Yhdisteolioiden luokka class Composite : public Component { typedef std::vector<component*> ComponentVector; private: ComponentVector children; public: void operation(); void add(component *component); void remove(component *component); }; Suunnittelumallit Adapter ja Composite 21
VII.3.5 Composite: Esimerkki (4) void Composite::operation() { } std::cout << "Composite " << this << std::endl; ComponentVector::iterator iter; for(iter = children.begin(); iter!= children.end(); iter++) { } (*iter)->operation(); void Composite::add(Component *component) { } children.push_back(component); Suunnittelumallit Adapter ja Composite 22
VII.3.5 Composite: Esimerkki (5) void Composite::remove(Component *component){ } ComponentVector::iterator iter; iter = std::find(children.begin(), children.end(),component); if(iter!= children.end()){ } children.erase(iter); Suunnittelumallit Adapter ja Composite 23
VII.3.5 Composite: Esimerkki (6) // Asiakasohjelma int main() { Component *leaf1 = new Leaf(); Component *leaf2 = new Leaf(); Component *composite = new Composite(); composite->add(leaf1); composite->add(leaf2); composite->operation(); composite->remove(leaf2); composite->operation(); } // Tuhotaan kekodynaamiset oliot return 0; Suunnittelumallit Adapter ja Composite 24
VII.3.6 Composite-mallin toteutuksesta Lapsiluokassa tarvitaan joskus viitettä vanhempaan Huolehdittava, että rakenteen eläessä viitteet pysyvät kunnossa Vaikuttaa komponenttien jakamiseen (useamman vanhemman ylläpitäminen hankalaa) Mitä operaatioita Component-luokkaan? Jälkeläisten käsittely tarpeetonta Leaf-luokissa: Suositaanko yhtenäisyyttä vai turvallisuutta? Voidaan tehdä oletustoteutukset, jotka määritellään uudelleen Composite-luokissa Kuka tuhoaa poistettavat oliot? Ei ongelma kielissä, joissa roskienkeruu Suunnittelumallit Adapter ja Composite 25