TIE-20200 Ohjelmistojen suunnittelu Luento 4 : MVC:t ja kumppanit Samuel Lahtinen TIE-20200 Samuel Lahtinen 1
Ajankohtaista Harjoitustyö Versiohallinnan kutsut lähetetty, ota yhteyttä jos et ole moista saanut Viikkoharkat alkavat, ensimmäisellä viikolla QML:n kanssa puuhastelua
Ohjelmassa tänään Riippuvuuksien syöttö - dependency injection tarkkailija MVC, MVVM, MVP, MV, jne.
Jotain yleisiä periaatteita Tällä kurssilla käydään läpi tekniikoita, joiden avulla vähän isompien ohjelmien suunnittelu, toteutus ja hallinta onnistuu paremmin Yleisesti: suunnittelun tulee olla tarkoituksenmukaista, turha geneerisyyden lisääminen ja epäoleellisuuksien kanssa nysvääminen pois. Tämä vielä tärkeämpää, jos tavoitteena on tehdä nopeasti ja kertakäyttöistä Kohdeympäristö, työkuorman jakaminen, jatkokehitys, ylläpito tuovat mukaan suunnittelutarvetta Tarkoituksenmukaisuus: ohjelma jolla voi etsiä tiedostoista avainsanoja Toteutus puhtaalla c++:lla ja jollain käyttöliittymäkirjastolla tuskaista Skriptikieli, komentoriviskripti, pari riviä (GUI wrapperin kera vähän enemmän)
Tietojen sitominen, käyttöliittymät Yleinen käyttöliittymäkirjastojen yms. kanssa Ideana sitoa käyttöliittymässä näkyvä/esitettävä asia ohjelmakoodin arvoihin, propertyihin tai toimintoihin Sen sijaan että esim. tekstikentän arvoa muutetaan koodissa kutsumalla esim. tekstikenttäolion palveluita, kerrotaan että tekstikentän arvo on sidottu merkkijonomuuttujan/propertyn arvoon jos tekstikentän arvoa muutetaan, muuttuu arvo koodissa. Jos arvo koodin puolella muuttuu, muuttuu arvo käyttöliittymässä. Sidonta mahdollista myös käyttöliittymäelementtien välillä, esim. taulukon koko sidotaan merkkijonoon tai elementin sijainti toiseen elementtiin.
Käyttöliittymäpuoli ja tietojen sidontaesimerkki, C#-versiona Property-jutut vielä
Dependency injection, riippuvuuksien syöttö Riippuvuuksien syöttö, ideana tarjota luokalle riippuvuudet rajapintojen kautta Perinteinen lähestymistapa, luokka nimeltään Palvelu ja sen käyttäjä PalvelunTarvitsija. Esimerkki: Luodaan Palvelun instanssi PalvelunKäyttäjän rakentajassa, käytetään jne. Suora riippuvuus tiettyyn toteutukseen, ei pelkästään rajapintaan Jos halutaan vaihtaa toteutus, pitää koskea myös PalvelunKäyttäjään Dependency injection: annetaan rajapintaan viite tai osoitin, esim. Luokan rakentajassa, käytetään sitä käyttäjäluokassa. Rajapinta ja sen toteutus erikseen, helpottaa ylläpitoa, laajentamista jne. Päästään ongelmasta, jossa luokka tulee automaattisesti riippuvaiseksi tietystä toteutuksesta (luonti jne.). Voidaan käyttää aliluokkia jne. Voidaan vaihtaa käytettävää toteutusta kesken ajon
Dependency injection ja C++ Jos käytetään osoittimia, C++:n kanssa voi tulla ongelmia (ei roskienkeruuta). Kuka vastaa tuhoamisesta? Kenen pitää kutsua deleteä jne. Fiksuilla osoittimilla pääsee isosta osasta näitä ongelmia (+samalla tulee dokumentoitua omistussuhteiden toimintaa) Dependency injectionia varten olemassa myös kirjastoja, joissa riippuvuuksien asettaminen tapahtuu kirjaston oman koodin kera. Osassa kirjastoista koodia tuotetaan tai riippuvuuksia voidaan asettaa asetustiedostoilla jne. ohjelman toimintaa voi muokata ilman kääntämistä (yksi esimerkki:https://code.google.com/p/wallaroo/) Javalle & C#:lle helpompaa, joten parempia/valmiimpia kirjastojakin enemmän Yksi mahdollisuus elinajan hallintaan, template ja omistajaluokka, jonka käyttäjäluokka perii ( perittyä luokkaa luodessa kerrotaan, mitä toteutusta käytetään), esimerkki myöhemmin
Tarkkailija TIE-20200 Samuel Lahtinen
Mistä tarkkailija tuttu Qt:n käyttäjille? TIE-20200 Samuel Lahtinen
Esimerkkiä pelin saavutukset (achievement) ja niiden hallinta Miten leipoisit saavutusten hallinnan mukaan peliin? Ääni, symbolin vilauttaminen, tallentaminen pelaajan profiilille, profiilin päivitys myös pelipalvelulle Mahdollisuus kontrolloida saavutuksia
Tarkkailija Määrittelee olioiden välille yksi moneen riippuvuuden siten, että kun yhden olion tila muuttuu, siitä riippuvat oliot saavat ilmoituksen Toteutustapoja: QT Signaalien ja Slotien avulla.net esim. eventtien, delegaattien avulla Java Rajapintojen avulla Observer, Observable/Subject Javascript, funktio-osoittimilla jne. C++, funktio-osoittimet, rajapinnat jne. Signal-slotit yms omat tavat saattavat vaikeuttaa koodin ymmärtämistä, tehdä suorituksen seuraamisesta vaativampaa erityisesti huolettomasti käytettyinä.
MVC Model View Controller Tunnetuin jne. Suunnittelumalli käyttöliittymän ja ohjelman muiden osien erittelyyn toisistaan. Erilaisia toteutustapoja yksityiskohtaeroin löytyy kasapäin, erilaisia ohjeita toteuttamiseen samoin Ideana ohjelma (tai ohjelmakomponentti) koostuu kolmesta osasta, joilla jokaisella oma tehtävänsä View-Näkymä: esittää tiedot käyttäjälle Controller-kontrolleri: käsittelee käyttäjän syötteet Model-Malli: huolehtii tiedon hallinnasta/käsittelystä
MVC Model View Controller Selitysosa 2. View: sisältää käyttöliittymäkomponentit, kutsuu kontrollerin tarjoamia palveluita liittämään käyttöliittymässä näkyvät asiat ohjelman varsinaisiin toimintoihin, esittää mallilta saamiaan tietoja Controller: tarjoaa näkymälle rajapinnan, käyttää mallia toteuttamaan tarjoamansa toiminnot, muuttaa mallia Model: sisältää ohjelman varsinaisen pihvin ( business logic ) ja talletettavat tiedot. Ei ole vain passiivinen tietovarasto. Tarjoaa näkymällä tietoja näkymän kannalta esitettävässä muodossa. Sovellusalueet ja käyttökohteet vaihtelevat, Perinteiset työpöytäohjelmat Ohjelmistokehykset, arkkitehtuurit Web-sovellukset, sivusto-palvelinsuhteet
Tyypillinen MVC:n osien välinen kommunikaatio päivittää Model Muuntaa, manipuloi View Controller näkee käyttää
Shakkitehtävä MVC ja shakki Mitä pistetään malliin, näkymään tai kontrolleriin?
Toteutustapoja, osa 1 Malli on passiivinen, tekee jotain vain jos näkymä tai kontrolleri kutsuvat sen palveluita Esim. Käyttäjä klikkaa nappulaa, tapahtuma ohjataan kontrolleriin, siellä kutsutaan mallista palvelua. Kun suoritus palaa takaisin kontrolleri kertoo näkymälle päivityskehotuksen. Näkymä pyytää mallista tarvitsemansa tiedot. Yksinkertainen toteutus erityisesti mallin toteuttamisen kannalta
Toteutustapoja, osa 2 Puoliaktiivinen malli (Pull MVC) Malli kertoo kiinnostuneille näkymille, että sen tila on muuttunut. Näkymä kysyy mallilta muuttuneen tiedon/tiedot ja päivittää käyttöliittymän (Observer-pattern ja tapahtumien kuuntelu/seuranta) Näkymä voi tarvittaessa optimoida päivitystä Mahdollistaa myös mallin suunnalta tulevat tapahtumat ja mallin puolella tehtävät pidemmät laskennat (joiden valmistumisesta voi ilmoitella)
Toteutustapoja, osa 3 Aktiivinen malli, (Push MVC) Malli kertoo kiinnostuneille muuttuneen tiedon suoraan kutsumalla palvelua, jonka avulla välittää tiedon. Näkymä päivittää käyttöliittymän tietojen mukaiseksi Missä muodossa ja minkäkokoisia tietomääriä voidaan siirtää?
Yleisiä huomioita MVC:stä Voidaan käyttää useita eri näkymiä Käyttöliittymä helposti päivitettävissä kun vaatimukset jne. muuttuvat Monimutkaistaa ohjelman rakennetta, lisää koodia Jatkuvat muutokset malliin ja suorituskyky, näkymät voivat mennä juntturaan jos mallista pusketaan päivityksiä jatkuvalla syötöllä
MVP, MVC-variantit, osa 1 Model-View-Presenter View: näyttää tiedot käyttäjälle, ohjaa käyttäjän tekemät komennot/toimet presenterille Presenter: toimii näkymän ja mallin välissä, noutaa tietoja mallista, muokkaa näkymälle sopivaksi, käsittelee käyttäjän komennot ja muokkaa mallia. Sisältää varsinaisen sovelluslogiikan Model: tarjoaa rajapinnan tiedolle ja sen käsittelylle (+sisältää tiedot jne)
Model View Presenter Passiivinen näkymä, näkymä vain antaa tietoa eteenpäin presenterin möyhittäväksi Presenter hoitelee kommunikaation, mallin ja näkymän välillä, toimintalogiikka (voi ohjata useampia näkymiä/malleja) Mallissa pysyvä tieto (sen käpistely) Tiedon sidonta mahdollista, mallin päivitykset osin suoraan näkymään http://martinfowler.com/eaadev/supervisingpresenter.html + http://martinfowler.com/eaadev/passivescreen.html MVC vs MVP, http://www.infragistics.com/community/blogs/todd_snyder/archive/2007/10/17/mvc-ormvp-pattern-whats-the-difference.aspx, http://www.codeproject.com/articles/288928/differencesbetween-mvc-and-mvp-for-beginners
Qt:n oma lähestymistapa Qt sisältää joukon komponentteja, jotka käyttävät Model/View arkkitehtuuria hoitamaan tietoa ja sen visualisointia Control-osa integroitu näkymään Lähinnä tekniikka, jolla taulukoita, kokoelmia jne. ja niihin liittyvää tietoa saa visualisoitua, järjesteltyä yms. http://doc.qt.io/qt-5/model-view-programming.html
Riippuvuuksien syöttäminen ja MVC Miten hyödyntäisit riippuvuuksien syöttämistä MVC:n tapauksessa? Esimerkkikoodia, vanhaa harkkatyötä
MVC-variantit, osa 2: Model-View-View-Model Microsoftin suosima arkkitehtuuriratkaisu.net ympäristöön Yleisidea: käyttöliittymä (XAML, peruskoodi) jossa käyttöliittymäelementit sidotaan näkymämallin tietoihin Varsinainen malli sisältää lasketantatoiminnallisuuden jne. Näkymämalli välissä toimii välikappaleena ja antaa tietoa ja toimintoja näkymälle (ja sen toteuttajalle) sopivassa muodossa Malli muuttaa näkymämallia, näkymä sidottu näkymämalliin, näkymä päivittyy automaattisesti. Näkymässä tapahtuu, näkymämallin kautta muutokset malliin, jossa voidaan tehdä varsinainen ohjelman toiminta
MVVM-arkkitehtuurikuvana view Data binding and commands View Model notifications View2.. updates notifications Model
MVVM luokkien välinen kommunikaatio Kuvan lähde: TIE-20200 Samuel Lahtinen http://msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx
Model-View-View-Model Hyvää: Käyttöliittymä voidaan eriyttää täysin muusta ohjelmasta Käyttöliittymän toteuttaminen onnistuu ilman varsinaista koodaamista, tehdään vain toimintojen ja asioiden sitomista näkymämalliin > käyttöliittymän toteuttajan ei tarvitse olla koodari Malli voidaan toteuttaa itsenäisesti, ei riippuvuuksia näkymään Testaus, näkymämallin testaus toiminnallisuuden testauksena Uudelleenkäyttö voi helpottua, kun näkymään liittyvät asiat eivät sotke Huonoa: Monimutkaisuus vaikeaselkoisuus, lisää työtä pienemmissä projekteissa Muuta: Sama idea toteutettavissa esim. Qt ympäristössä, ei tarvitse.net ympäristöä ja sen kaikkia kikkoja ollakseen käyttökelpoinen
MVVM näkymän tehtävät Näkymä on visuaalinen elementti, kuten ikkuna, sivu, kontrolli, datanäkymä. Näkymässä on mukana siihen kuuluvat kontrollielementit, layoutit ja tyylitiedot. Näkymä hoitaa käyttöliittymän visuaalisen toiminnan, animaatiot, tilamuutokset jne. (nämä voivat johtua joko näkymämalliin tulevista muutoksista tai käyttäjän tekemistä jutuista) Toteutuspuolella mukana voi olla tarvittaessa myös koodia, jolla hoidetaan asioita joita olisi mahdoton tai vaikea toteuttaa WPF:n, XAMLin avulla Näkymä viittaa näkymämalliin datacontext propertyn kautta. Näkymän kontrollit sidotaan propertyihin ja näkymämallin tarjoamiin komentoihin/funktioihin Näkymä voi erikoistaa sidonnan toimintaa näkymän ja näkymämallin välillä. Esim. Tiedon tyyppimuunnokset (merkijonosta kokonaisluku, päiväys) tai tehdä syötetiedoille validointia ja antaaa käyttäjälle enemmän tietoa.
MVVM näkymämallin tehtävät Näkymämalli ei näy käyttäjälle, eikä siinä ole mukana mitään käyttöliittymään liittyviä komponentteja (WPF, Silverlight, XAML ) Kapseloi tiedon esityslogiikan jota tarvitaan käyttötapauksen tai käyttäjän tehtävän toteuttamiseen Näkymämallin tulee olla testattavissa itsenäisesti (ilman näkymää tai mallia). Näkymämalli ei normaalisti käytä suoraan näkymää vaan toteuttaa propertyjä ja komentoja/palveluita joihin näkymää sidotaan. Näkymämalli kommunikoi näkymän kanssa tapahtumien avulla (INotifyPropertyChanged and INotifyCollectionChanged rajapinnat) Komennoista lisää (ICommand rajapinta, komento-oliot, Command Method komennot) Näkymämalli huolehtii näkymän ja mallin välisestä kommunikaatiosta. Se voi muuttaa tai käsitellä tietoa niin, että se on näkymälle helpommassa muodossa. Lisäksi näkymämalli voi tarjota propertyjä, joita ei ole olemassa varsinaisessa mallissa. Tiedon validointiin voidaan käyttää IDataErrorInfo or INotifyDataErrorInfo rajapintoja. Näkymämalli voi sisältää myös näkymää varten olevaa ohjelman tilainformaatiota
MVVM malli Malli sisältää ohjelman tiedon ja bisneslogiikan, hoitelee siis varsinaista ohjelman tietoa ja huolehtii siitä, että sitä käsitellään oikeilla algoritmeilla jne. toiminnallisuuden Mallit eivät viittaa suoraan mihinkään näkymän osaan tai näkymämalliin (ei riippuvuuksia näiden toteutustapaan) Kommunikaatio muihin kerroksiin käyttäen INotifyPropertyChanged and INotifyCollectionChanged rajapintoja. Tietokokoelmat (listat jne.) usein ObservableCollection<T> luokasta erikoistettuja Mallit käyttävät usein tietojen hakupalveluita tai tietokantoja, tiedon käsittelyn ja hakujen kapselointi (tietojen lopullinen verifiointi jne.)
MVVM, esimerkkiä Monet frameworkit esimerkiksi Javascriptille sisältävät valmiina MVC- tai MVVM-jaon Esimerkki, Knockout.js (tutorial 2), http://jsfiddle.net/nfzycs4k/
MVP, MVC, MVVM Mitä eroja?
Yhteenveto Käyttöliittymän erottaminen ohjelman muusta toiminnallisuudesta hyvä idea jos ollaan tekemässä hieman suurempaa projektia Mahdollista testata osakokonaisuuksia itsenäisesti Mahdollisuus toteuttaa käyttöliittymä ja muita osia itsenäisesti Muutokset käyttöliittymässä tai toiminnallisuudessa helpompia, kun molemmat eivät ole sikin sokin Olemassa monia erilaisia suunnittelumalleja, arkkitehtuurimalleja, joilla tämän voi tehdä MVC ja sen johdannaiset yleisiä monissa frameworkeissä, ekosysteemeissä, kirjastoissa, ei yleensä kannata tapella vastaan, vaan mennä niiden mukaan TIE-20200 Samuel Lahtinen 34