MVC programfelépítés megfigyelő minta használatával nem GUI alkalmazásban

Az előző néhány bejegyzésben azért jártuk körül részletesen a megfigyelő programtervezési mintát (observer design pattern), mert ezt a mintát lehet használni az MVC betűszóval jelölt programszerkezet megvalósítására is.

Az MVC a Model, View és Controller angol szavakból ered, és egy olyan programfelépítésre utal, aminek lényege, hogy a modellt, vagyis az adatfeldolgozást, annak szabályait és logikáját tartalmazó programrészt függetlenítse az adatok felhasználó számára történő megjelenítését végző programrészektől (View). A kettőt a vezérlő rész (controller) kapcsolja össze olyan módon, hogy fogadja a felhasználói kéréseket, a bemenő adatokat és azok alapján, illetve azokat használva kezdeményezi a modellnél az adatfeldolgozást vagy a modell bizonyos állapotának megváltozását.

Azáltal, hogy a modellt megvalósító és a megjelenítést, azaz a felhasználói interfészt képviselő programrészek függetlenek azzal az előnnyel jár, hogy a program így az igények változása tekintetében rugalmas lehet. Ha új fajta vagy kinézetű megjelenítés szükséges, akkor az úgy valósítható meg, hogy a modellhez nem kell nyúlni. Ez azt is jelenti, hogy a modell és a megjelenítést végző programrészek egymástól függetlenül fejleszthetők, ha a megjelenítendő adatok köre előre ismert.

Az MVC több módon is felépíthető. Miután a Controller fogadta a felhasználói kérést, a bemenő adatot, felkéri a Modellt az adatfeldolgozásra. Innentől viszont két eltérő alapváltozatot eredményezhet az, hogy hogyan kap egy megjelenítő (View) objektum értesítést arról, hogy módosítani kell az adatok felhasználó számára történő megjelenítésén:

  1. a Controller kapja vissza a Modelltől a megjelenítendő eredményt. Ezt követően a Conroller kéri a View objektumot vagy objektumokat, hogy jelenítésék meg a Modell által átadott eredményt az előírt módon.
  2. a Controller nem kapja meg a Modelltől az eredményt, hanem a Modell értesíti az egy vagy több View objektumot, hogy történt változás és ezt jelenítésék meg, vagy aktualizálják a felhasználó által látottakat.

A 2. esetben alkalmazható jól a megfigyelő minta. Ilyenkor a modell lesz a publikáló vagy más szóval a megfigyelt objektum, akihez a megjelenítő objektumok mint megfigyelők feliratkoznak. Ha a modellben állapotváltozás történik, akkor az értesítést küld a megfigyelőknek erről, akik ennek alapján aktualizálják a megjelenítést.

Az MVC architekturát általában grafikus felhasználói felülettel (GUI) történő megjelenítés esetén használják. De az elvek nem GUI esetén is alkalmazhatók, vagyis amikor a felhasználó számára az adatmegjelenítés a konzolon kiprintelt formában történik. Erre mutatunk egy példát most.

A példában egy autót, pontosabban annak gyorsítását vagy lassítását modellezzük úgy, hogy a Car nevű osztályban felveszünk egy speed nevű attribútumot, amely az autó mindenkor aktuális sebességét tartja nyilván km/h-ban. Az autó sebességét változtatni, azaz bizonyos értékkel gyorsítani vagy lassítani, az accelerate nevű metódussal lehet, amelynek argumentuma a kívánt sebességváltozás. Pozitív érték gyorsítást, negatív érték lassítást eredményez.

Azt szeretnénk, ha az autó aktuális sebességéről minden gyorsítás/lassítás után két formában is kapjunk tájékoztatást. Egyrészt lássuk egy egyszerű skálán valamilyen módon vizuálisan, másrészt szövegesen is olvashassuk az aktuális km/h sebességet, sőt kapjuk egy kiegészítő szöveges figyelmeztetést arról, ha egy előre beállítható megengedett maximális sebességet a pillanatnyi sebesség túllép.

A fenti igény azt jelenti, hogy minden egyes accelerate() hívás után két kiírás lesz a konzolon, amelyek mindegyike az aktuális sebességet jeleníti meg valamilyen formában. E két eltérő megjelenítésre két külön megjelenítő (View) objektumot definiálunk CarSpeedView1 és CarSpeedView2 osztálynevekkel. E két objektumot az autó sebességállapotának változásáról a megfigyelő mintát alkalmazva értesítjük. Ekkor a Car osztály lesz a megfigyelt objektum és így örökölni fog a korábbi bejegyzésekben is már látott Publisher osztályból. A CarSpeedView1 és CarSpeedView2 megfigyelők lesznek, amelyek a Subscriber absztrakt osztályt fogják örökölni, és így kötelezően implementálják az update_view() metódust, amely argumentumaként a megjelenítendő aktuális sebességet kapják meg a Car értesítésekor.

A Controller osztály feladata egyrészt, hogy a megfigyelőket a megfigyelt objektumhoz feliratkoztassa, másrészt fogadja a felhasználó által megadott gyorsítási értékeket, amivel a Car példány accelerate() metódusát meghívja.

A program teljes kódja és néhány megjelenő kiírás látható alább.

E program lényeges eleme a megfigyelő tervezési minta. Ha ennek működése esetleg e példából nem teljesen világos, akkor érdemes a korábbi „Értesítés küldés állapotváltozásról” kezdetű címmel írt bejegyzéseket elolvasni.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.