2. Digitális óra
28
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
2.1 A feladat Ebben a fejezetben egy viszonylag egyszerő problémára alkalmazva tekintjük át az OO tervezés modellezési technikáit. A feladat a 2.1. ábra szerinti digitális óra (pontosabban egy ilyen digitális órát a számítógép képernyıjén szimuláló szoftver) megtervezése.
HH.NN
ÓÓ:PP
SELECT
SET
ALARM ALARM ON/OFF
2.1. ábra: Digitális óra
Az ábrán az óra kijelzıje és a gombjai láthatók. Az óra – normál üzemmódban – arra szolgál, hogy a kijelzı HH.NN és ÓÓ:PP mezıiben a pontos dátumot/idıt mutassa. Az ALARM mezı onnan kapta a nevét, hogy abban az ALARM szöveg látszik (normál üzemmódban), ha az órán az idızített riasztás (ébresztés) be van kapcsolva. A három gomb különféle beállítási, kapcsolási lehetıségeket nyújt a felhasználónak. Az ALARM ON/OFF gomb az óra normál üzemmódjában használható: a felhasználó erre kattintva az idızített riasztást kapcsolhatja be vagy ki. (A bekapcsolás – ALARM ON – nem azonnali riasztást jelent, hanem azt, hogy az óra riasztani fog, amikor a pontos idı egyenlı lesz egy elıre adott értékkel, azaz a tervezett ébresztés/riasztás idıpontjával. A kikapcsolás – ALARM OFF – viszont akár az éppen folyamatban lévı riasztást – hangjelzést – is azonnal leállítja.) A SELECT gomb az óra bármely állapotában használható. Normál üzemmódban erre a gombra kattintás után az óra beállítási üzemmódba megy át, elsıként a hónapot (HH) lehet állítani, amit az is jelez, hogy ilyenkor a HH mezı tartalma villog. A beállítás úgy történik, hogy a felhasználó a SET gomb nyomkodásával egyesével lépteti a mezı értékét. – A SET gombra kattintás értelemszerően csak beállítási üzemmódban hatásos, és mindaddig ugyanannak a mezınek az értékét lépteti, amíg a felhasználó nem kattint ismét a SELECT gombra. A SELECT gomb második alkalmazása után a nap (NN), harmadik után a pontos idı óra (ÓÓ), negyedik után a pontos idı perc (PP) állítható. A SELECT gomb ötödik alkalmazása után ismét az ÓÓ mezı tartalma léptethetı, de már nem mint a pontos idı órája, hanem a riasztási idı órája, amit az is jelez, hogy ilyenkor az ÓÓ mezı tartalma az ALARM felirattal együtt villog. A SELECT gomb hatodik alkalmazása után ismét a PP mezıben a riasztási idı perc értéke léptethetı (közben a PP mezı tartalma az ALARM felirattal együtt villog). Beállítási üzemmódban bármely mezı értékének léptetése ciklikus: például a 12. hónap után az 1. hónap következik, vagy a 23 óra után 0 óra következik. A beállítási üzemmódot a SELECT gomb hetedik alkalmazása zárja le, az óra ekkor visszatér normál üzemmódba.
29
DIGITÁLIS ÓRA
2.2 A probléma használati eset modellje Egyszerő problémáról lévén szó, a követelmények felmérése mellızhetı, hiszen az lényegében ugyanazokat a tényeket és elvárásokat tárná fel, amelyeket már a 2.1. alfejezet feladatleírása is tartalmaz. Ami a felhasználói beavatkozás lehetıségeirıl elmondható, az is mind kiderül a 2.1. alfejezetbıl, tehát a funkcionális követelményeket specifikáló használati eset modellnek sincs érdemi jelentısége, ezért csupán ujjgyakorlatként következik itt a probléma használati eset diagramja – mindjárt két változatban. A 2.2. ábra diagramja azt mutatja, hogy az órán megtekintheti a pontos idıt (Normál használat), illetve opcionálisan ki-/bekapcsolhatja a riasztást, vagy kezdeményezheti az óra beállítását. 2.2. ábra: A digitális óra használati esetei – vázlatos változat
A 2.3. ábra szerinti diagramváltozat több információval szolgál, mert a beállítás módját is részletezi. Kiderül belıle, hogy normál üzemmódban közvetlenül csak a hónap beállítása kezdeményezhetı; ha viszont ebbe belépett a felhasználó akkor kénytelen végigmenni az összes beállítási lépésen, hogy viszszatérhessen az óra normál üzemmódjába. Azt is kifejezi az ábra, hogy amikor a felhasználó belép például a Nap beállítása használati esetbe, akkor ott nem feltétlenül fogja változtatni a nap sorszámát.
2.3. ábra: A digitális óra használati esetei – részletezı változat
30
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
Ellenırzı/magyarázó kérdések (mindegyik kérdés a 2.3. ábrára vonatkozik): 1. Az egyik «extend» sztereotípusú függés Mert a Hónap beállítása használati eset képezi opcionális kiterjesztését a miért a Hónap beállítása Normál Normál használat használati esetnek. használat irányítású? 2. A Hónap beállítása és a Riasztás Ha az óra mőködik, és a felhasználó sembe-/kikapcsolása használati esetek mi- mit sem csinál, akkor az óra a Normál ért csak a Normál használatból érhetık használat esetnek megfelelı üzemmódel? – A felhasználó miért nem léphet be köz- ban van (függetlenül attól, hogy a felhaszvetlenül ezekbe is? (Ez nem mond ellent an- náló megnézné, mennyi az idı). Tehát az a nak, hogy a felhasználó anélkül is ki-/bekap- lényeg, hogy a felhasználó mindenképpen csolhatja riasztást, hogy megnézné az aktuá- a Normál használat használati esetben lisan mutatott idıt?) kezdeményezheti a Hónap beállítása vagy a Riasztás be-/kikapcsolása használati eseteket, akkor is, ha egyébként nem veszi igénybe a Normál használatra jellemzı szolgáltatást. 3. A Hónap beállítása miért opcionális Mert a felhasználó úgy is használhatja az órát, hogy azt (soha) nem állítja be. kiterjesztése a Normál használatnak? Mert a Hónap beállítása használati 4. Az egyik «include» sztereotípusú fügeset kötelezıen tartalmazza a Nap beálgés miért a Hónap beállítása Nap lítása használati esetet. Más megfogalbeállítása irányítású? mazásban: ha a felhasználó belépett a Hónap beállítása használati esetbe, akkor azt kötelezı neki a Nap beállítása használati esettel folytatni. (Éppen megtehetné azt is, hogy a végtelenségig a Hónap beállítása üzemmódban hagyja az órát, de annak semmi értelme.) 5. Mivel fejezi ki az ábra, hogy amikor a fel- Azzal, hogy az Érték léptetése haszhasználó belép például a Nap beállítása nálati eset csupán opcionális kiegészítése a használati esetbe, akkor ott nem feltétlenül különféle beállítások használati eseteinek. fogja változtatni a nap sorszámát? (Lásd az «extend» sztereotípust!) 6. Milyen jelentısége/haszna lehet annak, Az egyik jelentısége kitőnik a 5. kérdésre adott válaszból. További hasznának megérhogy az Érték léptetése használati téséhez tudni kell, hogy a diagramok (így a esetet szerepeltetjük a diagramon? használati eset diagram) szimbólumaihoz különféle (pl. szöveges) specifikációk főzhetık. Ha valahol le akarjuk írni, hogy az egyes értékek a Set gomb nyomkodásával léptethetık, akkor az egy helyen az Érték léptetése használati esethez kapcsolva specifikálható (2.4. ábra). E használati eset hiányában a specifikációt mindegyik beállítást szolgáló használati esetnél meg kellene ismételni.
DIGITÁLIS ÓRA
2.4. ábra: Szöveges specifikáció főzése az Érték léptetése használati esethez a CASE eszköz Properties ablakában
31
32
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
2.3 A probléma statikus modellje 2.3.1 A statikus modell elsı változata Bonyolultabb rendszerek statikus modelljének – osztálydiagramjának (osztálydiagramjainak) – szerkesztését esetleg konkrét objektumok kiragadott konkrét kapcsolatait mutató objektumdiagram(ok) tanulmányozásával kell elıkészíteni, és akkor az osztálymodell egy vagy több objektumdiagramból vonatkoztatható el. – Van olyan esettanulmány a kötetben, amelynél így járunk el, azonban a digitális óra annyira kevés objektum kevés számú kapcsolataként áll elı, hogy azonnal belevághatunk az osztálydiagram szerkesztésébe. A megértés megkönnyítése céljából fokozatosan, több változatot kidolgozva jutunk el a „végsı” megoldáshoz. A feladat leírásából azonnal azonosítható a probléma objektumainak háromféle típusa, azaz osztálya: a gombok ( Gomb osztály), a kijelzı ( Kijelzı osztály), a mezık mint a kijelzı komponensei ( Mezı osztály). Azonban az csak a látszat, hogy a digitális óra 2.1. szakaszban leírt mőködése ezen háromféle alkatrész együttmőködésébıl elıállhat; az is csak látszat, hogy a gombok nyomkodására közvetlenül a kijelzı reagál. Efféle kijelzıket ugyanis úgy éri meg „gyártani” (kifejleszteni), ha azok sokféle célra felhasználhatók: nem csak digitális órához, hanem pl. zsebszámológéphez, digitális láz-, vérnyomás- vagy vércukorszintmérıhöz is. Ebbıl az következik, hogy a kijelzıt kézenfekvıen csak azokkal a képességekkel célszerő felruházni, amelyekre a felsorolt (és más fel nem sorolt) eszközök mindegyikében szükség lehet. Másképpen fogalmazva: a kijelzı nem lehet felelıs a digitális óra sajátos képességeiért; tehát a 2.1. alfejezetben leírt mőködés még egy alkalmas vezérlıegységet (agyat) is igényel. Azt az osztályt, amelynek példánya a digitális óra agya (vezérlıegysége), a továbbiakban DigitálisÓra osztálynak nevezzük. Megjegyzés: Megállapodunk, hogy a digitális óra alatt a teljes szerkezetet értjük, míg a DigitálisÓra osztály példányai csak a digitális óra vezérlésére alkalmas objektumok (közülük egy digitális óra csak egy példányt tartalmaz). A 2.5. ábra az osztálydiagram elsı változatát mutatja. Ennek megértését segítik a következı ellenırzı/magyarázó kérdések. Ellenırzı/magyarázó kérdések (mindegyik kérdés a 2.5. ábrára vonatkozik): Éppen lehetne, de kevesebbet mondana: 1. A DigitálisÓra és a Gomb osztályok nem fejezné ki, hogy három különbözı között a három asszociáció helyett nem rendeltetéső gombra van szükség. (Persze, lehetne egyetlen asszociáció a Gomb felıli ha 3 helyett 103 gomb lenne a rendszerben, végén 3-as számossággal? akkor nem törnénk annyira magunkat, hogy megkülönböztessük ıket.) Hasonlóan indokolható a Kijelzı és a Mezı közötti öt asszociáció. 2. Nem kellett volna erısebben hangsúlyozni Nem, mert a gombok különbözısége nem a gombok rendeltetésbeli különbségeit azzal, típusszintő, hanem csak példányszintő. Az hogy azokat a Gomb osztályból származtatott lenne típusszintő különbség, ha a különbözı gombok eltérı példányattribútumokkal különbözı osztályokból vesszük? vagy eltérı példánymőveletekkel rendelkeznének, vagy bizonyos mőveleteket más-
DIGITÁLIS ÓRA
33
képpen valósítanának meg. Ezek a gombok azonban ugyanazzal az attribútummal rendelkeznek (címke), és csupán az attribútum értékében különböznek, ami csak példányszintő különbség.
2.5. ábra: A digitális óra statikus modellje, 1. változat – Mőveletek nélküli osztálydiagram
3. Mi a magyarázata annak, hogy a DigitálisÓra és a Gomb osztályok közötti asszociációk nem irányítottak (pontosabban a navigációjuk kétirányú) ellentétben a többi asszociációval?
Ezen asszociációk navigációja azért kétirányú, mert 1. a DigitálisÓra-példánynak mint kompozíciónak látni kell a komponenseit képezı Gomb-példányokat, hiszen általában a kompozíció hozza létre a komponenseit; 2. a Gomb-példánynak látni kell az ıt használó DigitálisÓra-példányt, különben nem tudja neki jelezni, hogy a felhasználó éppen rákattintott. Ezzel szemben például a DigitálisÓra és a Kijelzı osztályok közötti asszociáción a navigáció egyirányú, mert mindig a
34
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
4. Mit jelent az, hogy a hónapMezı szerepnevet egy, a Kijelzı és a Mezı osztályok közötti asszociációnak a Mezı osztály felıli vége kapta?
5. Mi a magyarázata annak, hogy a DigitálisÓra és a Gomb osztályok közötti asszociációk mindkét végén látható szerepnév ellentétben a többi asszociációval?
6. A hónap, a nap, a pontosÓra, a pontosPerc, a riasztásÓra és a riasztásPerc miért nem a Kijelzı attribútamai, ha egyszer ezek az értékek mind a kijelzın jelennek meg?
1
DigitálisÓra-példány kéri fel valamilyen mőveletre a Kijelzı-példányt. Ez két dolgot jelent: 1. A Kijelzı osztálynak lesz egy hónapMezı nevő attribútuma. 2. A hónapMezı nevő attribútum Mezı típusú, azaz az attribútum tartalma vagy a Mezı egy példánya, vagy egy olyan mutatóérték, amely a Mezı egy példányára mutat.1 Ezt is az magyarázza, hogy a DigitálisÓra és a Gomb osztályok közötti asszociációkon a navigáció kétirányú: A digÓra szerepnév feltüntetésének az a következménye, hogy a Gombnak lesz egy digÓra nevő példányattribútuma, amely DigitálisÓra típusú, azaz az attribútum tartalma egy olyan mutatóérték, amely a DigitálisÓra egy példányára mutat. A select szerepnév feltüntetésének az a következménye, hogy a DigitálisÓranak lesz egy select nevő példányattribútuma, amely Gomb típusú, azaz az attribútum tartalma vagy a Gomb egy példánya, vagy egy olyan mutatóérték, amely a Gomb egy példányára mutat. A Kijelzı példányai „buta” objektumok, amelyek csak annyit tudnak, hogy megjelenítik a megfelelı mezıjükben a nekik paraméterként átadott szöveg (String) típusú adatot. A hónap, a nap, a pontosÓra, a pontosPerc, a riasztásÓra és a riasztásPerc viszont csupa szám (int) típusú adat, hiszen különben nem lehetne egyesével léptetni. Egy Kijelzı-példány például azt sem képes eldönteni, hogy az óraMezıben aktuálisan a pontosÓra vagy a riasztásÓra értékét kell-e megjeleníteni. A felsorolt értékeknek a select és a set
A programozási környezettıl és a programozó döntésétıl függ, hogy az alternatív megoldások közül melyik valósul meg. Java nyelv használata esetén csak a második helyen említett eset lehetséges, azaz pl. egy Mezı típusú attribútum a Java nyelvben soha nem egy Mezı-példányt, hanem csak egy Mezı-példány mutatóját tartalmazza. Más nyelvekben a programozó egy kompozíció megvalósítására vonatkozóan dönthet úgy, hogy a kompozíció (esetünkben a Kijelzı-példány) közvetlenül a komponenseit tartalmazza (tehát közvetlenül egy Mezı-példányt nem pedig annak mutatóját).
DIGITÁLIS ÓRA
35
gombokkal kezdeményezett változásait csak a DigitálisÓra-példány tudja levezényelni, így ezeknek szükségképpen a DigitálisÓra példányattribútumainak kell lenni. Történetesen az NN érték állításakor a Kijelzı-példány napMezıjében látszó értéket is a DigitálisÓra-példány lépteti olymódon, hogy a saját nap attribútumának értékét növeli eggyel, majd ezt a nap értéket kétkarakteres szöveggé konvertálja pl. így: String(nap,”99”), és ennek két számjegybıl álló szöveges kimenetét küldi ki megjeleníteni a kijelzıre. (A String(…) függvényt lásd a 2.4.2. szakaszban.)
7. Mi a rendeltetése a mezık érték, látszik és villog nevő attribútumainak?
Az érték attribútum tartalmazza a mezıben megjelenítendı tartalmat szöveg formában. A látszik attribútum igaz (true) értéke esetén az érték attribútummal adott tartalom látszik a mezıben; hamis (false) értéke esetén nem látszik. – Ennek a kapcsolónak az alarmMezı esetében van jelentısége, a többi mezınél ugyanis folyamatosan látszik=true. A villog attribútum igaz (true) értéke esetén a mezıben látszó tartalom villog, ellenkezıleg nem villog.
A kapott statikus modell a késıbbi dinamikus modellezés során finomodik, további elemekkel bıvül; az egyes osztályok felelısségi körébe tartozó mőveletek többsége is általában a dinamikus modellezés útján azonosítható. Azonban a DigitálisÓra egy példánymőveletét és valamennyi osztály konstruktorát már itt felvesszük a modellbe. Technikai megjegyzés: A konstruktor bárhogyan elnevezhetı, ezt nem szabályozza az UML. Ezzel szemben a Java nyelvben a konstruktor neve kötelezıen azonos az osztály nevével. Mi az esettanulmányainkban ezt a Java nyelvbıl átvett szabályt fogjuk követni. Az elıbbi megjegyzés alapján a DigitálisÓra osztály konstruktora a DigitálisÓra() nevő mővelet. Mivel DigitálisÓra-példány a konstrukció agya, ezért a kezdeti állapotának beállítása, akár a külvilágtól kapott paraméter nélkül is beállítható, ezért a konstruktora nem használ paramétert. A gombokat már a DigitálisÓra-példány hozza létre, és ı közli az új gombbal, hogy mi legyen a címkéje: „SELECT”, „SET” vagy „ALARM ON/OFF”. Tehát a gomb konstruktorának lesz legalább egy szöveg (String) típusú paramétere: pCímke:String. A gombnak látnia kell a DigitálisÓra-példányt, hogy jelezni tudja neki, amikor a felhasználó rákattintott, ezért a DigitálisÓra-példány a saját mutatóját (belsı azonosítóját) is közli az új gombbal. Ez egy újabb paraméter: pDigÓra:DigitálisÓra. Amikor a gomb jelez a DigitálisÓra-példánynak, akkor valahogy azonosítania kell magát, hogy ı éppen melyik gomb (milyen eseményt jelez). Erre a célra a gombnak kell legyen még egy
36
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
attribútuma, nevezzük eseményKódnak. A konstruktor ennek értékét is beállítja, tehát szükség van egy harmadik paraméterre is: pEseményKód:int2. A gomb konstruktorának teljes szignatúrája tehát: Gomb(pCímke:String, pDigÓra:DigitálisÓra, pEseményKód:int). Tegyük fel, hogy a gomb úgy jelez a DigitálisÓra-példánynak, hogy egy eseményFeldolgozás(eseményKód) üzenetet küld neki. Ez esetben a DigitálisÓra definíciójának tartalmazni kell egy eseményFeldolgozás(eseményKód:int) mőveletet. Az eseményFeldolgozás mővelet lehetséges mőködését (megvalósítását) a következı pszeudokódú3 kifejtés világítja meg: eseményFeldolgozás(eseményKód:int): void { HA eseményKód = 1 AKKOR selectFeldolgozás() KÜLÖNBEN HA eseményKód = 2 AKKOR setFeldolgozás() KÜLÖNBEN HA eseményKód = 3 AKKOR alarmOnOffFeldolgozás(); }
2.6. ábra: A digitális óra statikus modellje, 1. változat – Mőveletekkel kiegészített osztálydiagram
Térjünk vissza a konstruktorokhoz! A mezı konstruktorának szignatúrája kézenfekvıen: Mezı(pÉrték:String, pLátszik:boolean, pVillog:boolean). A három paraméter abból adódik, hogy a mezı mindhárom attribútumának be kell állítani a kezdı értékét. A mezıket ugyan a Kijelzı-példány hozza létre, de neki is a DigitálisÓrapéldány mondja meg, mit tegyen a mezıivel. Ebbıl adódóan a legtöbb paramétert a kijelzı 2 3
Az int egész szám adattípust jelent (az integer rövidítése). Pszeudokód: leírását és értelmezését lásd az 1.4.2. szakaszban.
DIGITÁLIS ÓRA
37
konstruktorának kell átadni: a kijelzı mind az öt mezıje mindhárom attribútumának kezdı értékét, ami összesen 15 (primitív típusú skalár4) paraméter. Azonban formailag egyszerősíthetı a mezı konstruktorának szignatúrája5, ha a sok primitív skalár paraméter helyett egy olyan paraméterobjektumot (legyen a neve: pKijelzı) használunk, amely az attribútumaiban éppen az említett 15 skalár értéket tartalmazza. Ezzel a kijelzı konstruktorának szignatúrája következı lesz: Kijelzı(pKijelzı:ParaméterKijelzınek). Megjegyzés: Ezen a ponton a pKijelzı paraméterobjektumot nem azért vezetjük be, mert az minden tekintetben elınyösebb lenne, hanem csak azért, hogy azzal tömörítsük a Kijelzı konstruktorának (késıbb pedig a többi mőveletének) a szignatúráját. A digitális óra programozása szempontjából nem biztos, hogy ez a legjobb megoldás, illetve egy elıregyártott Kijelzı osztály újrafelhasználása esetén, a Kijelzı specifikációja is korlátozza, hogy a paraméterre melyik megoldás alkalmazható.
Ellenırzı/magyarázó kérdések folytatása (mindegyik kérdés a 2.6. ábrára vonatkozik): digÓra.eseményFeldolgozás(1) Magyarázat: A gombnak az a dolga, hogy az általa látott DigitálisÓra-példányt felszólítsa egy mővelet végrehajtására, ezért szerepel a mővelet címzettjeként a digÓra objektum. Ezt az objektumot kéri meg az objektum az eseményFeldolgozás mővelet végrehajtására az 1-es eseménykód paraméterrel. A pszeudokódú kifejtésbıl kiderül, hogy az 1-es eseménykód azt jelzi a digÓra objektumnak, hogy aktuálisan a select gomb szólt hozzá, tehát az eseményFeldolgozás mőveleten belül a selectFeldolgozás mőveletet kell végrehajtania. 9. Mi az oka annak, hogy a DigitálisÓra Az eseményFeldolgozás mőveletre a külvilág (egy másik osztályú objektum) mőveletei között az eseményFeldolgozás publikus, viszont a selectFeldol- szólítja fel a DigitálisÓra-példányt. Ez azonban csak úgy lehetséges, ha ez a mőgozás, a setFeldolgozás és az velet publikus. alarmOnOffFeldolgozás mőveletek Az eseményFeldolgozás mővelet privátak? pszeudokódú kifejtésébıl kiderül, hogy a selectFeldolgozás mővelettel a DigitálisÓra-példány önmagát bízza meg, tehát ezt a mőveletet a külvilágnak nem kell ismernie, az privát lehet. Ugyanez a magyarázat a másik két privát mőveletre is. A selectFeldolgozás(), 10. A selectFeldolgozás(), setFeldolgozás(), … metódusok a setFeldolgozás(), … miért a DigitálisÓra példányattribútumain DigitálisÓra példánymőveletei, miért
8. Ha a felhasználó rákattint a select gombra, akkor az milyen mőveletet hajt végre?
4
A primitív típus és a skalár értelmezését lásd az 1.4.2. szakaszban.
5
A mővelet szignatúrája értelmezését lásd az 1.4.2. szakaszban.
38
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
nem közvetlenül a megfelelı gomb hajtja végre ezeket? 11. A modell szerint hány példányattribútuma van a Kijelzı osztálynak? (Ezt a kérdést a 2.5. ábrára vonatkozóan is fel lehetne tenni.)
végeznek mőveleteket, így azok csak a DigitálisÓra metódusai (mőveletei) lehetnek. Öt. Ugyanis a Kijelzı és a Mezı közötti asszociációkon megadott hónapMezı, napMezı, … szerepnevek egyúttal a Kijelzı példányattribumai.
2.3.2 A statikus modell második változata – újrafelhasználás I. Ez a feladat jó alkalmat ad az újrafelhasználás bemutatására is, azaz olyan megoldásra, amikor mások által korábban kifejlesztett osztályt (vagy osztályokat) használunk fel az alkalmazásunk felépítéséhez. Bizonyos típusú objektumok sokféle alkalmazásban elıfordulnak. Így például a funkciógombok minden, grafikus felhasználói felülettel rendelkezı alkalmazásnak komponenseit képezik. Tehát biztosan lehet találni egy mások által már kifejlesztett Gomb osztályt. Persze ennek az osztálynak a tervezıi nem gondolhattak a gombok olyan felhasználására, amikor azok éppen egy digitális óra alkatrészei, tehát a Gomb osztályt nem tervezhették a digitális óra speciális igényei szerint; másrészt úgy kellett megtervezniük, hogy annak példányai bármilyen alkalmazás felhasználói felületébe beilleszthetık legyenek. Tegyük fel, hogy egy – a már készen adott Gomb osztályból vett – példány csak a következıt tudja: ha a felhasználó rákattint, akkor a gomb felkér egy objektumot az eseményFeldolgozás(eseményKód) mőveletre. E mőveletet végrehajtó objektumnak a mutatóját a gomb figyelı nevő attribútuma tartalmazza. Az ilyen gomb azért használható bármilyen környezetben, mert a Gomb osztály tervezıi a figyelı típusát nem konkrét osztállyal, hanem egy interfésszel (tehát egy absztrakt osztállyal) adták meg (ennek neve legyen: EseményFigyelı), amely csupán az egyetlen eseményFeldolgozás nevő absztrakt mővelettel rendelkezik. A Gomb osztály figyelıBejegyzés(pFigyelı) mővelete arra szolgál, hogy a figyelı attribútum tartalmát beállítsa a pFigyelı paraméterben kapott értékre. Ebben a paraméterben a gombbal az ıt figyelni hivatott objektum mutatója közölhetı, azaz az EseményFigyelı valamely realizációja megfelelı példányának mutatója. Az elıregyártott Gomb osztály valamely példányának a DigitálisÓra egy példányával való összekapcsolására elsı közelítésben az a megoldás kínálkozik, hogy a DigitálisÓra osztályt az EseményFigyelı interfész realizációjaként tervezzük meg (lásd a 2.7. ábrát); közelebbrıl ez azt jelenti, hogy a DigitálisÓra rendelkezik az eseményFeldolgozás mővelet megvalósításával, amely egyébként azonos a 2.3.1. szakaszban pszeudokóddal adott megoldással. Ezt követıen a DigitálisÓra-példányt kell a gombok figyelıjeként beállítani.
DIGITÁLIS ÓRA
2.7. ábra: A digitális óra statikus modellje, 2. változat – elıregyártott Gomb osztállyal
Ellenırzı/magyarázó kérdések (mindegyik kérdés a 2.7. ábrára vonatkozik): 1. Ebben a modellben miért van szükség az Mert a fejlesztıi a Gomb osztályt úgy EseményFigyelı osztályra? tervezték meg, hogy annak példánya egy EseményFigyelı-példányt szólít meg, valahányszor rákattintnak.
39
40
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
2. Mit csinál a Gomb példánya a figyelıBejegyzés() paraméterében neki átadott értékkel? 3. Ebben a modellben mi a Gomb osztály figyelı nevő attribútumának a statikus típusa? 4. Ebben a modellben mi lehet egy Gombpéldány figyelı nevő attribútumának a dinamikus típusa?
Beírja a figyelı nevő attribútumába.
EseményFigyelı. Ugyanis a figyelı a Gomb EseményFigyelı asszociációban az EseményFigyelı szerepneve. DigitálisÓra. Magyarázat: Az EseményFigyelı egy interfész, tehát nem lehet közvetlen példánya, vagyis csak olyan példánya lehet, amely valamely realizációjának a példánya. Ebben a modellben az EseményFigyelınek csak egyetlen realizációja van: a DigitálisÓra. 5. Mi az oka annak, hogy a 2.6. ábrával ellen- Ha a 2.7. ábrán is kétirányúak lennének ezek az asszociációk, akkor Gomb osztály tétben a 2.7. ábrán a DigitálisÓra és a rendelkezne DigitálisÓra típusú attGomb közötti asszociációk csak egyirányúak? ribútummal; azonban a Gomb osztályt nem így tervezték. 6. Ha a DigitálisÓra és a Gomb közötti De, látják. A DigitálisÓra-példány az asszociációk csak egyirányúak, mégpedig a EseményFigyelı osztálynak is példáGombra irányítottak, akkor ebben a modellnya, mert a DigitálisÓra realizációja ben a Gomb-példányok nem látják a Digiaz EseményFigyelınek. A gombok éptálisÓra-példányt? pen a DigitálisÓra-példányt látják a figyelı szerepben. 7. Mi az oka annak, hogy a 2.6. ábra modell- Mert ebben a modellben a Gomb osztályjével ellentétben itt nem három, hanem csak nak nincs olyan digÓra példányattribúkét paramétere van a Gomb konstruktorának? tuma, mint a 2.6. ábra szerinti modellben, így nincs szükség arra a harmadik paraméterre, amely a digÓra attribútumba írandó értéket hordozná. 1. válasz: Mert a Gomb osztályt valaki ko8. A Gomb-példány figyelı nevő rábban így tervezte meg. attribútumának értékét miért nem a Gomb 2. válasz: Ez a megoldás lehetıvé teszi a konstruktora állítja be, miért kellett erre a már létrehozott gomb objektum figyelıjécélra még a figyelıBejegyzés(…) nek utólagos lecserélését. Ennek ugyan a mőveletet is bevezetni? digitális óra esetében nincs semmi jelentısége, de más probléma megoldásánál határozottan elınyös lehet. 9. Amikor a DigitálisÓra-példány kiadja a A kérés címzettje a select gomb. select.figyelıBejegyzés(pFigyelı) A pFigyelı paraméter értéke a kérést, akkor melyik objektum a kérés DigitálisÓra-példány mutatója. címzettje, és milyen konkrét érték lesz a A 6. kérdésre visszatérve, a gomb most is pFigyelı paraméter tartalma? látja a DigitálisÓra-példányt, mert az egyik attribútuma tartalmazza annak mutatóját, de ez most nem a digÓra attribútum, hanem a figyelı attribútum.
DIGITÁLIS ÓRA 10. Az EseményFigyelı neve és az ugyanezen osztály definíciójában az eseményFeldolgozás() neve miért dılt betővel látszik? 11. A modellben az EseményFigyelı osztálynak miért nincs konstruktora?
41
Mert ez jelzi, hogy az EseményFigyelı egy absztrakt osztály, és az eseményFeldolgozás() egy absztrakt mővelet. Mert az egy absztrakt osztály, amelynek nem lehet közvetlen példánya. (A közvetett példányát pedig az EseményFigyelı valamelyik realizációjának konstruktora hozza létre.)
2.3.3 A statikus modell harmadik változata – újrafelhasználás II. Akár a 2.3.1. szakasz modelljében, akár a 2.3.2. szakasz modelljében a DigitálisÓrapéldány arra kényszerül, hogy valahányszor a felhasználó rákattint valamely gombra, és emiatt a gombtól hozzá egy eseményFeldolgozás(eseményKód) üzenet érkezik, mindannyiszor megvizsgálja, hogy a háromféle gomb közül aktuálisan melyik szólította meg, azaz újra és újra döntenie kell, hogy a selectFeldolgozás(), a setFeldolgozás() és az alarmOnOffFeldolgozás() mőveletei közül aktuálisan melyiket kell végrehajtania. Ettıl elegánsabb lenne, ha a DigitálisÓra-példányhoz a SELECT gombtól eleve a selectFeldolgozás() üzenet, a SET gombtól a setFeldolgozás() üzenet, az ALARM ON/OFF gombtól pedig az alarmOnOffFeldolgozás() üzenet érkezne. – Hogy lehet ezt megoldani? Hogyan lehet elérni, hogy ugyanazon Gomb osztály különbözı példányai különbözı módon viselkedjenek, ha ráadásul ezt az osztályt nem is mi terveztük? Kis kitérı: Mitıl „elegánsabb” az egyik megoldás a másiknál? – Attól, hogy vele valamilyen cél kevesebb ráfordítással érhetı el, vagy attól, hogy vele az eredeti célnál messzebbre is el lehet jutni. Tehát az elegánsabb megoldás valamilyen szempontból hatékonyabb, elınyösebb is. Esetünkben az elegánsabbnak mondott megoldás azzal a haszonnal járna, hogy egy – a futási idıben – gyakran ismétlıdı döntést feleslegessé tenne. Persze konkrétan a digitális óra esetében a gombesemény azonosítását szolgáló döntések kiküszöbölése nem vezet a felhasználó által is érzékelhetı látványos javuláshoz, hiszen a digitális óra élettartamának jelentéktelen része telik azzal, hogy egy felhasználó kattintásaira kell reagálnia. Ha azonban a digitális óra helyett egy olyan tranzakciókezelı rendszert képzelünk el, amelynek folyamatosan több ezer felhasználó által kezdeményezett mőveleteket kell végrehajtania, annál a feleslegesen ismétlıdı mőveletek kiiktatásának haszna a válaszidı nagyon is észrevehetı javulásában mutatkozna meg. A megoldást a 2.8. ábra mutatja. Ez a 2.7. ábra modelljétıl abban különbözik, hogy most nem a DigitálisÓra képezi az interfész realizációját, hanem helyette három különbözı realizációt specifikálunk: a SelectFigyelı, a SetFigyelı és az AlarmOnOffFigyelı osztályokat. A lényeg, hogy így mindegyik gombnak saját speciális figyelıje, az eseményFeldolgozás(…) mőveletnek pedig három, különbözı megvalósítása lesz. Az eseményFeldolgozás() megvalósítása a SelectFigyelı osztályban: eseményFeldolgozás(eseményKód:int): void { digÓra.selectFeldolgozás() }
42
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
2.8. ábra: A digitális óra statikus modellje, 3. változat – Interfész többféle realizációval
DIGITÁLIS ÓRA
43
Az eseményFeldolgozás() megvalósítása a SetFigyelı osztályban: eseményFeldolgozás(eseményKód:int): void { digÓra.setFeldolgozás() } Az eseményFeldolgozás() megvalósítása az AlarmOnOffFigyelı osztályban: eseményFeldolgozás(eseményKód:int): void { digÓra.AlarmOnOffFeldolgozás() } Mint az a megvalósítások pszeudokódjából is kitőnik mindhárom eseményfigyelınek látni kell a digÓra objektumot, mert azt kell felszólítaniuk a megfelelı mőveletre. Továbbá az is látszik, hogy ha a select gomb figyelıjének a SelectFigyelı példányát képezı selectF objektum állítódik be (például így: select.figyelıBejegyzés(selectF)), akkor a select gombra kattintáskor a digÓra éppen a selectFeldolgozás() mőveletre lesz felszólítva. Hasonlóak mondhatók el a set gomb és a setF eseményfigyelı összekapcsolásáról, valamint az alarmOnOff gomb és az alarmOnOffF eseményfigyelı összekapcsolásáról. Ellenırzı/magyarázó kérdések (mindegyik kérdés a 2.8. ábrára vonatkozik): 1. Ebben a modellben hány attribútuma van a Tizennégy: A DigitálisÓra dobozban DigitálisÓra osztálynak? felsorolt hét attribútumon felül az osztály asszociációinak túl végén álló szerepnevekbıl keletkezı attribútumok: kijelzı, select, set, alarmOnOff, selectF, setF és alarmOnOffF. Nem feltétlenül. 2. Az EseményFigyelı interfésznek Választhattunk volna olyan megoldást is, feltétlenül három különbözı realizációját hogy az EseményFigyelı interfészhez, kellett felvenni a modellbe? – Más módon nem lehetett volna elérni, hogy az elırecsak egy GombFigyelı realizációt defigyártott Gomb osztályból vett három gomb niálunk (amely a 2.3.2. modelljével ellentéthárom különbözı mőveletre szólítsa fel a ben most nem azonos a DigitálisÓra DigitálisÓra-példányt? osztállyal), és ebben az eseményFeldolgozás(eseményKód) mővelet megvalósítását a 2.3.1. szakaszban olvasható pszeudokód szerint adhatnánk meg. (Így a GombFigyelı-példány közvetítésével a különbözı gombok különbözı mőveletekre szólítják fel a DigitálisÓra-példányt?) 3. Az elızı. kérdésre adott válasz ellenére mi Az elızı kérdésre adott válaszban szereplı egyetlen realizáció (a GombFigyelı) nem az oka annak, hogy az EseményFigyelı interfésznek három különbözı realizációját tette mellızhetıvé az eseményKód tesztevettük fel a modellbe? lésén alapuló döntéseket. Ez a megoldás csak abban a különbözik a 2.3.2. szakasz modelljétıl, hogy nem a DigitálisÓrapéldány, hanem a GombFigyelı-példány végez felesleges mőveleteket. A lényeg, hogy az eseményKód vizsgálatát
44
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
csak a háromféle realizációval lehetett kiküszöbölni. Ez nem hiba. 4. Nincs hiba az eseményFeldolgozás() mővelet itt adott háromféle megvalósításának A paraméter csak azért szerepel a mővelet szignatúrájában, mert az elıregyártott pszeudokódjában? – Gondoljon arra, hogy EseményFigyelı interfész tervezıi az ennek a mőveletnek van egy paramétere, de eseményFeldolgozás(eseményKód) azt a pszeudokód szerint a mővelet mégsem használja fel! mőveletet paraméterezettre specifikálták. Ugyanakkor az a megoldás, hogy az interfészre háromféle realizációt adtunk, az eseményKód paraméter használatát feleslegessé tette. Onnan, hogy ezt a DigitálisÓra-pél5. Honnan tudja a select gomb, hogy egy dány közvetlenül a létrehozása után egy rákattintás alkalmával neki a selectF select.figyelıBejegyzés(selectF) figyelıobjektumot kell felkérnie az eseüzenettel közli vele. Az üzenet következtéményFeldolgozás() mővelet végrehajben a select gomb figyelı attribútutására? mának értéke a selectF objektum mutatója lesz. EseményFigyelı. Ugyanúgy, mint a 6. Ebben a modellben mi a Gomb osztály 2.3.2. szakasz modelljében. (Lásd az ottani 3. figyelı nevő attribútumának a statikus kérdésre adott választ!) típusa? A Gomb osztály figyelı nevő példány7. Ebben a modellben egy Gomb-példány attribútumának a dinamikus típusa az figyelı nevő attribútumának mi lehet a EseményFigyelı interfész valamelyik dinamikus típusa? realizációja. A select gomb esetében ez a SelectFigyelı, a set gomb esetében a SetFigyelı, az alarmOnOff gomb esetében pedig az AlarmOnOff Figyelı. 1. válasz: Mert a Gomb osztályt egykori 8. A Gomb osztályt miért az EseményFifejlesztıi így írták meg. gyelı interfésszel köti össze egy asszociá2. válasz: Az alternatív megoldás azt jelenció? – Másképpen: a Gomb osztályt miért nem inkább a SelectFigyelı, a SetFi- tené, hogy minden Gomb-példány három eseményfigyelı objektummal lenne összegyelı és az AlarmOnOffFigyelı oszkapcsolva, holott egy Gomb-példánynak tályokkal kapcsolja össze egy-egy asszociácsak egy megfelelı eseményfigyelı objekció? tumot kell látnia. 9. A DigitálisÓra osztályt miért nem az Az egyik irányban a DigitálisÓra-példány hozza létre az interfész realizációoszEseményFigyelı interfésszel köti össze tályainak selectF, setF és alarmOnegy asszociáció (a Gomb osztály mintájára), miért az interfész realizációival áll asszociá- OffF példányait, tehát kell lenni a Digicióban? tálisÓra osztályból a realizáció osztályok felé mutató egy-egy asszociációnak. Ellenkezı irányban a selectF, setF és alarmOnOffF példányoknak is látni kell a digÓra objektumot, de ezt nem fejezheti ki az interfészbıl a DigitálisÓra felé
DIGITÁLIS ÓRA
10. A DigitálisÓra és az interfész realizációi közötti asszociációk miért kétirányúak?
11. Mi az oka annak, hogy a korábbi modellekkel ellentétben ebben a modellben a selectFeldolgozás(), setFeldolgozás() és alarmOnOffFeldolgozás() mőveletek publikusak?
45
mutató asszociáció, mert az ehhez főzött szerepnévbıl következıben az EseményFigyelınek rendelkeznie kellene egy DigitálisÓra típusú példányattribútummal. Ezzel szemben egy interfésznek nem lehet attribútuma. Tehát egy megoldás marad: a DigitálisÓra és az interfész realizációk közötti három, kétirányú asszociáció. Az egyik irányt az indokolja, hogy a DigitálisÓra-példány hozza létre az interfész realizációosztályainak selectF, setF és alarmOnOffF példányait; a másik irányt pedig az, hogy a selectF, setF és alarmOnOffF objektumok kérik fel mőveletre a digÓra objektumot. Mert ezen mőveletek végrehajtására a DigitálisÓra-példányt más osztályok példányai szólítják fel.
Bár idáig a digitális óra probléma statikus modelljének több változatát is kidolgoztuk, mégsem tartunk a „végleges” modellnél. A következı alfejezetben tárgyalt dinamikus modellezésnek az objektumok viselkedésének megtervezésén túl az is lényeges célja, hogy a statikus modellt finomítsa, annak esetlegesen hiányzó elemeire fényt derítsen. 2.3.4 Amit a tervezı elhanyagolt Ha az idáig leírtakat egy gyakorlott programozó olvassa, akkor elszörnyőlködik azon, hogy a tervezı mennyi mindenrıl megfeledkezett. Ugyanis a megvalósítás szintjén lényegesen több részletet kell meghatározni, aminek következtében az említett osztályoknak is több attribútuma, illetve több mővelete lesz. DigitálisÓra osztály A feladat leírása szerint a kifejlesztendı digitális óra a fizikai órának egy grafikus felületen való szimulációja, vagyis egy speciális funkciójú képernyıablak, angolul és a legtöbb fejlesztı környezet terminológiájában: egy form. Egy formról pedig meg kell mondani, hogy milyen mérető legyen (szélesség, hosszúság), milyen színő legyen, stb. Tehát a megvalósítás során a DigitálisÓra osztályt ilyen – a grafikus megjelenítést leíró – attribútumokkal is el kell látni. Mivel a formok minden grafikus felület nélkülözhetetlen fogalmai, minden grafikus fejlesztıkörnyezet rendelkezik elıregyártott Form osztállyal (vagy más nevő, de hasonló rendeltetéső osztályal). Tehát a megvalósítás során az újrafelhasználás a DigitálisÓra osztály megkonstruálására is alkalmazható, amennyiben az az elıbb említett Form osztályból származtatható, és attól azonnal megörökli a szélesség, a hosszúság és a szín attribútumokat, amelyek konkrét értékei a kivitelezı egy grafikus fejlesztı felületén néhány egér-
46
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
kattintásal pillanatok alatt beállíthatja, és a látványt, ami a beállítás eredménye, mindjárt meg is nézheti. A Gomb osztály és a gomb objektumok elhelyezése a formon. Ahogy a formnak van mérete, színe, úgy gombnak is rendelkezni kell ilyen vizuális attribútumokkal, sıt az utóbbinak kétféle színe is van, egyik a háttérszín, a másik a címke betőinek színe, továbbá jellemzi még egy koordinátapár is, amely a formon jelöli ki a gomb helyét. Ezekkel sem foglalkozott a tervezés, de a kivitelezık részére manapság rendelkezésre álló grafikus fejlesztı felületek mellett ezek a hiányosságok pillanatok alatt pótolhatók: a kivitelezı a grafikus vezérlıelemek palettájáról egérrel a form megfelelı helyére húz egy gombot, ezzel annak máris beállította a koordinátáit, majd néhány további egérmőveletel beállítja a gomb méreteit, és így tovább. Azzal sem foglalkozott a tervezés, hogy egy gomb általában nem egy, hanem többféle esemény küldésére képes, mint (jobbkezes egér esetén) például: szimpla kattintás a bal egérgombbal, dupla kattintás a bal egérgombbal, kattintás a jobb egérgombbal, … – A tervezı által készített modellek csak egyféle kattintás eseményt feltételeznek, ezt a kivitelezı megfeleltetheti a bal egérgombbal végzett szimpla kattintásnak, a további eseménytípusokra pedig a digitális óránk esetében nincs is szükség. A Kijelzı osztály és a Mezı osztály, a kijelzı és a mezık elhelyezése a formon. Az elıbbiek mintájára a Kijelzı és a Mezı osztályok sem nélkülözhetik a vizuális jellemzıket (koordináták, méret, háttérszín, karakterek színe). Ezek beállítása is a kivitelezıre hárul, és a kivitelezınek a grafikus fejlesztı felületen végrehajtandó akciói is hasonlóak az elıbbiekben leírtakhoz: a kijelzı objektumnak vizuálisan megfelelı grafikus panel objektum, valamint a mezı objektumok a grafikus vezérlıelemek palettájáról húzhatók rá a formra. Összefoglalva: A statikus modell a digitális óra belsı mőködésére koncentrált és elhanyagolta azokat a külsı látványelemeket, amelyek a legtöbb grafikus-vizuális fejlesztı felületen viszonylag kevés ráfordítással – néhány egérmővelettel – kialakíthatók.
DIGITÁLIS ÓRA
47
2.4 A probléma dinamikus modellje Az objektum szerkezet és viselkedés egysége. A statikus modell a digitális óra szerkezetérıl szólt, ebben az alfejezetben dinamikus modellezési technikákkal a digitális óra viselkedését tárjuk fel, illetve tervezzük meg. 2.4.1 A digitális óra létrehozása, kezdeti teendık Elsı lépésként leírjuk azt a mőveletsort, amelynek eredményeként elıáll a digÓra objektumból és a hozzá kapcsolódó komponensekbıl a 2.7. modell szerinti szerkezető együttes. Éppen ez a mőveletegyüttes a DigitálisÓra() konstruktor tartalma. – Tegyük fel, hogy egy szekvenciadiagrammal el akarjuk mondani a fejlesztıknek, hogyan kódolják a DigitálisÓra() konstruktort. Ezzel a céllal született a 2.9. ábra. Magyarázatul főzzük hozzá: a folyamatot úgy kell elképzelni, hogy egy keretprogram megfuttatja a DigitálisÓra() konstruktort (ezt a részletet nem mutatja a 2.9. ábra), aminek hatására létrejön a digÓra:DigitálisÓra kompozícióobjektum, és az (még a DigitálisÓra() konstruktor végrehajtásának keretében) elvégez olyan kezdeti teendıiket, mint a saját attribútumok (hónap, nap, pontosÓra, …) kezdıértékének beállítása; a gombok, az eseményfigyelık és a kijelzı létrehozása. Tehát az utóbbiak konstruktorait már a digÓra futtatja meg. 1. megjegyzés: A diagramon néhány öndelegáció szimbólum ténylegesen nem öndelegációt jelöl, csak azért alkalmaztuk ıket, hogy az adott helyen részletezhessük, hogyan reagál az üzenetre az éppen megszólított objektum. (Ezzel a trükkel a kötetben gyakran fogunk élni.) 2. megjegyzés: A 2.9. ábra szerkesztıje némileg spórolt az energiájával. Így a három eseményfigyelı objektum létrehozásából csak a selectF:=SelectFigyelı(this) konstruktorhívás hatását részletezte. (Ebben a pDigÓra paraméter helyén a this érték áll, ami a 2.3.1. szakaszban rögzített megállapodás szerint a hívó objektum, azaz a DigitálisÓra-példány saját mutatója. – A DigitálisÓra-példány így közli a saját mutatóját az eseményfigyelı objektummal.) A konstruktorhívás hatása a részletezés szerint az, hogy a pDigÓra paraméterben átadott érték beíródik a létrehozott selectF objektum digÓra attribútumába. Hasonló hatást váltanak ki a setF:= SetFigyelı(this) és az alarmOnOffF:=AlarmOnOffFigyelı(this) konstruktorhívások is. – A három gomb létrehozásából is csak a select:=Gomb(”SELECT”, 1) konstruktorhívás hatása, valamint az éppen létrehozott select gombnak címzett figyelıBejegyzés( selectF) mővelet hatása van részletezve. Ezek szerint a konstruktor pCímke paramétere helyén adott ”SELECT” szöveg a gomb címke attribútumába, a pEseményKód paraméter helyén adott 1-es érték a gomb eseményKód attribútumába íródik be; a pFigyelı paraméter helyén megadott selectF értéket pedig a select gomb figyelı attribútuma veszi fel. A szerkesztı csak egy megjegyzésdobozban utalt rá, hogy (ugyan más paraméterértékekkel) hasonló mőveletek történnek a másik két gomb létrehozásánál is. 3. megjegyzés: A 2.9. ábra teljesen elhanyagolja a folyamat három lépésének részletesebb kifejtését. Az egyik a saját attribútumok kezdıértékének beállítása. A második a pKijelzı paraméterobjektum felépítése, amelynek részletezését úgy sikerült mellızni, hogy azt a digÓra öndelegációjaként tüntettük fel. (Ezzel azt is megspóroltuk, hogy a pKijelzı objektumot szerepeltetni kelljen a szekvenciadiagramon a példaobjektumok között.) A harmadik a Kijelzı(pKijelzı) konstruktor végrehajtásának kifejtése. (Ennek mellızésével megúsztuk, hogy még a kijelzı öt mezıjével is bıvíteni kelljen a diagramot.) – Az itt
48
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
elhanyagolt részleteket pótolni lehetne egy külön diagramon, de ezt sem tesszük, mert még nem vagyunk biztosak benne, hogy a digÓra és a kijelzı közötti kommunikációban tényleg a legjobb megoldás-e a pKijelzı paraméterobjektumot alkalmazni (ennek osztályát még fel sem vettük a statikus modellbe). Az egyetlen paraméterobjektum helyett többféle más megoldás is kínálkozik, a közülük való választás további elemzéseket igényel.
2.9. ábra: A digitális óra kezdeti teendıinek leírása szekvenciadiagrammal
DIGITÁLIS ÓRA
49
Ellenırzı/magyarázó kérdések (mindegyik kérdés a 2.9. ábrára vonatkozik): 1. A szekvenciadiagramokon a példaobjektu- Ez jelzi, hogy a példaobjektumok közül a mok általában egy szinten helyezkednek el. folyamat elején csak a digÓra létezik, az Mi az oka annak, hogy a 2.9. ábrán viszont a összes többi csak késıbb jön létre. Az, hogy selectF objektum nem a digÓra objeka selectF objektum a selectF:= tummal, hanem a SelectFigyelı(this) üzenettel áll selectF:=SelectFigyelı(this) egy szinten, közelebbrıl azt is jelzi, hogy ez üzenettel áll egy szinten? (Hasonló kérdések az üzenet éppen a selectF objektumot fogalmazhatók meg a többi komponensre is.) létrehozó konstruktorhívás. 2. Mi lesz a set:=Gomb(”SET”, 2) Létrejön a set gomb, és annak két attribúüzenet hatása? tuma értéket kap a két paramétertıl: set.címke:=”SET”. set.eseményKód:=2. 3. Mi lesz a set gombnak címzett A set.figyelı:=setF értékadás. figyelıBejegyzés(setF) üzenet hatása? Ez jelzi, hogy a digÓra egy vezérlı objek4. Mit fejez ki az, hogy a digÓra, a tum; a select egy határfelületi (felhaszselectF és a select objektumokat különbözı alakú szimbólumok képviselik a nálói felületi) objektum; a selectF pedig diagramon? egy sztereotípussal nem minısített objektum. 5. Ebben az együttmőködésben miért a Mert az összes többi objektumot ı hozza létre. (Ez a minısítés tehát független attól, digÓra számít vezérlı objektumnak? hogy a késıbbiekben a digÓra abban az értelemben vezérelt „szolgává” válik, hogy ı teljesíti a felhasználónak a gombok útján jelzett igényeit.) 6. Mit fejez ki az, hogy a szaggatott vonallal Ez a kérdés lényegében az 1. kérdés alterjelölt nyíl nem életvonalban végzıdik, hanem natív megfogalmazása. A kérdezett jelölés közvetlenül objektumra mutat? azt fejezi ki, hogy a nyíl egy konstruktort képvisel, azaz a nyíl végével mutatott objektum még nem létezik, hanem éppen a nyíllal képviselt üzenet hozza létre. (Ennek van egy alternatív jelölési módja is, amelynél a nyíl életvonalban végzıdik, de «create» sztereotípussal látjuk el.) A programozás iránt is érdeklıdı olvasók kedvéért a következı pszeudokódú részlet érzékelteti, hogy a 2.9. ábra szekvenciadiagramja alapján, hogyan kódolhatják a DigitálisÓra() konstruktort a programozók (a // jel után mindenütt magyarázó megjegyzés áll): DigitálisÓra() { // Itt lenne a saját attribútumok kezdıértékének beállítása, // de azt nem részletezzük. // this: a DigitálisÓra() konstruktorral éppen létrehozott // digÓra objektum mutatója. selectF:=SelectFigyelı(this); // selectF létrehozása setF:=SetFigyelı(this); // selectF létrehozása alarmOnOffF:=alarmOnOffFigyelı(this); // alarmOnOffF létrehozása select:=Gomb(”SELECT”, 1); // select gomb létrehozása
50
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK select.figyelıBejegyzés(selectF); // A select gomb figyelıje a selectF lesz. set:=Gomb(”SET”, 2); // set gomb létrehozása set.figyelıBejegyzés(setF); // A set gomb figyelıje a setF lesz. alarmOnOff:=Gomb(”ALARM ON/OFF”,3); // Az alarmOnOff gomb létrehozása alarmOnOff.figyelıBejegyzés(alarmOnOffF); // Az alarmOnOff gomb figyelıje az alarmOnOffF lesz. // Itt a pKijelzı felépítése következne, de nincs kifejtve. kijelzı:=Kijelzı(pKijelzı); // A kijelzı létrehozása
}
Az EseményFigyelı interfész három realizációjának konstruktora (a 2.9. ábra szekvenciadiagramjának értelmezése alapján) mindössze ennyibıl áll: SelectFigyelı(pDigÓra:DigitálisÓra) { digÓra:=pDigÓra; } SetFigyelı(pDigÓra:DigitálisÓra) { digÓra:=pDigÓra; } AlarmOnOffFigyelı(pDigÓra:DigitálisÓra) { digÓra:=pDigÓra; }
A Gomb(pCímke, pEseményKód) konstruktor feltételezhetı kódja (a 2.9. ábra szekvenciadiagramjának értelmezése alapján): Gomb(pCímke:String, pEseményKód: int) { címke:=pCímke; eseményKód:=pEseményKód; }
A Gomb osztály figyelıBejegyzés(pFigyelı) mőveletének feltételezhetı kódja (a 2.9. ábra szekvenciadiagramjának értelmezése alapján): figyelıBejegyzés(pFigyelı: EseményFigyelı): void { figyelı:=pFigyelı; }
2.4.2 A DigitálisÓra osztály állapotdiagramja A digitális óra komponensei közötti együttmőködés leírására késıbb még további szekvenciadiagramok is készülnek, elıbb azonban a DigitálisÓra állapotdiagramját szerkesztjük meg, mert az ennek útján szerzett ismeretekre a további szekvenciadiagramok szerkesztésénél is szükség lehet. A 2.1. alatti feladatleírás alapján az állapotdiagram könnyen elkészíthetı. Azonban az Olvasóban felmerülhet egy olyan kérdés, hogy a 2.1. alatt leírtakat miért a DigitálisÓra, miért nem inkább a kijelzı állapotváltozásainak kell tekinteni. – Nos, a szerkezet bármiféle állapotváltozásáról a felhasználó valóban csak a kijelzı útján értesülhet, de ez nem jelenti azt hogy a 2.1-ben leírt rendben folyó változások a kijelzı természetét képeznék. Ennek belátásához elég arra gondolni, hogy ha csak a magában álló kijelzın múlna, akkor az a hónap mezıben a 12. hónap után 13., sıt akár 99. hónapot is tudna mutatni, vagy akár számjegyektıl különbözı karaktereket is. A kijelzı története csak arról szól, hogy ha egy mezıjének címezve kap valamilyen tartalmat, azt abban a mezıben meg fogja jeleníteni, bármi legyen is az a
DIGITÁLIS ÓRA
51
tartalom. Tehát az, hogy a kijelzı mit mutat, nem rajta múlik, hanem az ıt vezérlı DigitálisÓra-példányon.
2.10. ábra: A DigitálisÓra-példány(ok) állapotdiagramja
A 2.9. ábra szerinti szekvenciadiagram (mint általában a szekvenciadiagramok) több objektum együttmőködésének ábrázolására alkalmas; ezzel szemben a 2.10. ábrán látható állapotdiagram csak egyfajta objektum, egy DigitálisÓra-példány életrajzának bemutatására szolgál. Ugyanakkor az állapotdiagram egy típusszintő (osztályszintő) modell, mert a vele közöltek általánosan – a DigitálisÓra osztály minden példányára – érvényesek, ezzel szemben a szekvenciadiagramok nem típusszintő modellek, mert velük az objektumok közötti együttmőködésnek csak egy-egy kiragadott konkrét esete írható le. Mivel a DigitálisÓra-példány a teljes szerkezet agya, az ı életrajzát (azaz az ı lehetséges állapotait) áttekintı állapotdiagram egyben a teljes szerkezet mőködése lényeges eseteinek számba vételére is alkalmas. Tehát ebbıl az állapotdiagramból vonhatók le következtetések arra is, hogy a digitális óra szerkezetén belül a komponens objektumok együttmőködésének mely eseteit szükséges mélyebben megvizsgálni és leírni például szekvencia-
52
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
diagramok segítségével. (Ez azt is magyarázza, az állapotdiagram kedvéért miért szakítottuk meg a szekvenciadiagramok sorozatának szerkesztését.) A 2.10. ábrán a DigitálisÓra() konstruktort nem csak azért tüntettük fel, hogy jelezzük: a DigitálisÓra-példányt melyik konstruktor hozza létre (az enélkül is triviális tény); hanem hogy rámutathassunk: egy sor kezdeti teendı, illetve változás ebben a konstruktorfutásban van elrejtve. Így megúsztuk, hogy a kezdetekre jellemzı olyan részleteket, amelyeket a 2.9. ábrán már kifejtettünk, ezen a diagramon ismételten szerepeltessünk; de elhanyagoltunk olyan részleteket is, amelyekrıl a 2.9. ábra sem szólt, és amelyekre a 2.4.1. szakaszban a DigitálisÓra() konstruktor pszeudokódjában is csak megjegyzésben utaltunk. Ezeket itt szöveges leírással pótoljuk: A DigitálisÓra() konstruktornak be kell állítani a digÓra kezdeti állapotát, de a Kijelzı (pKijelzı) konstruktor megfuttatásával a kijelzı kezdeti állapotát is (az összes mezıjének kezdeti állapotával / tartalmával együtt). Itt kell gondoskodni arról, hogy induláskor (tehát még azelıtt, hogy a felhasználó beállította volna) milyen idıpontot mutasson az óra. Egy lehetséges megoldás a következı: 1. Mivel egy szoftverrel szimulált digitális órát tervezünk ez egyszerően a futtató számítógéptıl (annak operációs rendszerétıl) lekérdezheti a rendszeridıt. Az így visszakapott értéket felhasználva beállítható a hónap, nap, pontosÓra és a pontosPerc. 2. A riasztásIgenNem kezdeti értéke false. Ez áll összhangban azzal, hogy a konstruktornak megfelelı nyíl a Riasztás kikapcsolva állapotba mutat. 3. Miután riasztásIgenNem = false, mindegy, hogy mi a riasztásÓra és a riasztásPerc értéke, tehát mindkettı lehet akár 0 is. 4. Össze kell állítani a Kijelzı(…) konstruktor paraméterét (vagy paramétereit), és a konstruktort megfuttatva létre kell hozni a kijelzı komponenst. – Ennek részletezését még mindig késıbbre halasztjuk. (Nem mintha a megoldás bonyolult lenne, hanem mert még felismerésre várnak a többféle lehetıség közötti választás szempontjai.)
Ellenırzı/magyarázó kérdések: 1. A digitális óránk milyen idıt mutat, amíg a felhasználó nem végez beállítást? (Itt a 2.10. ábrán felül figyelembe kell venni a fenti keretezett, szöveges leírást is.) 2. Ha a felhasználó úgy kapcsolja be a riasztást, hogy elıtte még soha nem állította be a riasztás idıpontját, mikor fog riasztani az óra? (Itt is figyelembe kell venni a fenti keretezett, szöveges leírást is.) 3. Ha az óra egyszer elkezdi a riasztást, meddig tart a hangjelzés?
Ugyanazt, mint az operációs rendszer órája (percnyi pontossággal).
Abban az idıpontban, amelyet a DigitálisÓra() konstruktor állít be a riasztásÓra és a riasztásPerc attribútumokba írt értékekkel.
Az állapotdiagram szerint kétféleképpen lehet vége: vagy úgy, hogy a felhasználó az ALARM ON / OFF gombra kattint (lásd az alarmOnOffGomb eseményt); vagy legkésıbb egy perc múlva, a legközelebbi percÓraJel esemény bekövetkezésekor. 4. A diagramon két Start pszeudoállapot Az, amelyikbıl a konstruktorral kiváltott átmenet indul, a DigitálisÓra-példány látható. Miben különbözik a jelentésük? életének (mőködésének) kezdetét jelzi. Az, amelyik a Riasztás bekapcsolva állapoton belül van, ezen szuperállapoton belüli kezdı alállapotot mutatja.
DIGITÁLIS ÓRA 5. A diagramon látható egy frissítés() mővelet és egy frissítés(pKijelzı) mővelet is. Mit gondol, ezek (1) azonos mőveletek, vagy (2) semmi közük egymáshoz, vagy (3) különbözı mőveletek, de az elıbbi végrehajtásának része az utóbbi végrehajtása?
53
A (3) eset áll fenn: A paraméter nélküli frissítés() mővelet elıtt nem áll objektumhivatkozás, ezért a végrehajtója az az objektum, amelyrıl az állapotdiagram szól, azaz a DigitálisÓrapéldány. A paraméteres változat kijelzı.frissítés(pKijelzı)alakban szerepel, tehát a végrehajtója a kijelzı objektum. A következı keretezett részben álló pszeudokód mutatja, hogy a DigitálisÓra osztályban definiált frissítés() mőveletet a DigitálisÓra-példány a kijelzı.frissítés(pKijelzı) kéréssel fejezi be.
OSZTÁLY DigitálisÓra { ... PRIVÁT frissítés(): void { pontosPerc := pontosPerc+1; HA pontosPerc = 60 AKKOR { pontosPerc := 0; pontosÓra := pontosÓra+1; HA pontosÓra = 24 AKKOR { pontosÓra := 0; nap := nap+1; HA nap > maxNap(hónap) AKKOR { nap := 1; hónap := hónap+1; HA hónap > 12 AKKOR hónap := 1; pKijelzı.hónapMezıÉrték := String(hónap,”99”); // A paraméterobjektumban a hónap aktualizálása } pKijelzı.napMezıÉrték := String(nap,”99”); // A paraméterobjektumban a nap aktualizálása } pKijelzı.óraMezıÉrték := String(pontosÓra,”99”) // A paraméterobjektumban a pontosÓra aktualizálása } pKijelzı.percMezıÉrték := String(pontosPerc,”99”) // A paraméterobjektumban a pontosPerc aktualizálása kijelzı.frissítés(pKijelzı); } } 1. megjegyzés: A fenti pszeudokódban a String(számérték, ”99”) függvény a számértéket szöveggé konvertálja. A második paraméter a kimenet formátumát definiálja: a ”99” formátum, azt jelzi, hogy a kimeneti szöveg pontosan két számjegybıl állhat. Tehát, ha a számérték 7, akkor a kimenet a ”07” szöveg lesz. 2. megjegyzés: A pszeudokódban a maxNap(hónap) függvény az adott hónapban legnagyobb napsorszámot adja vissza. Például a 3. hónapban 31-et, a 4. hónapban 30-at. (Február hónap esetén – az operációs rendszertıl lekérdezhetı – aktuális évszámot is figyelembe veszi.) 3. megjegyzés: A pszeudokódban nyomon követhetı, hogyan áll elı a pKijelzı paraméterobjektumban az az új tartalom, amelyet a DigitálisÓra-példány a frissítés(pKijelzés) üzenettel kiküld a kijelzıre.
54
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
6. Nem gondolja, hogy a 2.10. ábra diagramja nincs összhangban a 2.8. ábra osztálydiagramjával, mert nem mutatja, hogy a selectFeldolgozás(), a setFeldolgozás() és az alarmOnOffFeldolgozás() mőveletekre mikor szólítja fel a környezete a DigitálisÓra-példányt.
Formailag tényleg nincs teljes összhangban a két diagram, mert a 2.10. ábrán sehol nincs hivatkozás a kérdésben említett mőveletekre. A 2.11. ábra az állapotdiagram olyan újabb változatát mutatja, amely igazodik a statikus modellhez. Ehhez nem is kellett sokat tenni, csak a következı cseréket elvégezni: selectGomb selectFeldolgozás() setGomb setFeldolgozás() alarmOnOffGomb alarmOnOffFeldolgozás()
A 2.11. ábrán csillagok hívják fel a figyelmet a 2.10. ábrához képest megváltozott részekre.
2.11. ábra: A DigitálisÓra állapotdiagramja – összhangba hozva a statikus modellel
55
DIGITÁLIS ÓRA 7. A Hónap állítása állapotdobozban mit jelent a setFeldolgozás()/hónapLéptetés()
akcióspecifikáció? Nem lenne jobb: setFeldolgozás()/setFeldolgozás()
vagy egyszerően csak setFeldolgozás()?
Az objektumok közötti kommunikációban az objektum1 üzenetet küld az objektum2-nek, ez az üzenet az objektum2 számára egy esemény, amelyre általában valamilyen mővelettel reagál. Általános modellezési gyakorlat, hogy ebben a szituációban az üzenetet, az eseményt és a mőveletet pontosan ugyanúgy jelölik. A kérdésben említett esetben a megfelelı eseményfigyelı objektum setFeldolgozás() üzenetet küld a digÓra objektumnak, ez az utóbbinál egy setFeldolgozás() esemény, amelyre ı a setFeldolgozás() mővelettel reagál. Tehát teljesen korrekt lenne a setFeldolgozás()/setFeldolgozás()
akcióspecifikáció, sıt annak a rövidebb setFeldolgozás()/ változata is. Azonban a setFeldolgozás()/hónapLéptetés()
specifikáció többet mond. Azt fejezi ki, hogy a setFeldolgozás() mővelet az adott állapotban a hónapLéptetés() mőveletre szorítkozik. 2.4.3 A állapotdiagram alapján levonható következtetések A dinamikus modellezés egyik lényegi célja, hogy kiegészítse, finomítsa a statikus modellt. A 2.10-2.11. állapotdiagramokról azonnal látszik, hogy a DigitálisÓra és a Kijelzı osztályok milyen új mőveletekkel bıvülnek. Némileg több megfontolással adódik, hogy a DigitálisÓra osztályt egy további attribútummal (lásd fókusz) is ki kell egészíteni. Az eredményt a 2.12. ábra mutatja. DigitálisÓra ... - fókusz: int ... + percÓraJel(): void - frissítés(): void - hangJelzés(): void - hónapLéptetés(): void - napLéptetés(): void - pontosÓraLéptetés(): void - pontosPercLéptetés(): void - riasztásÓraLéptetés(): void - riasztásPercLéptetés(): void
Kijelzı ... + frissítés(pKijelzı: ParaméterKijelzı): void
2.12. ábra: A statikus modell bıvítése az állapotdiagrammal felderített új elemekkel
Azonban még módosítunk egy kicsit a modellen. Az olyan tevékenységeket, mint a hangJelzés(), vagyis amelyeket egy adott állapotban végig folytatni kell, könnyebb megvalósítani egy entry akcióként alkalmazott bekapcsolás mővelettel és egy exit akcióként alkalmazott kikapcsolás mővelettel, ahogy az az állapotdiagram érintett részének a 2.13. ábra szerinti módosításán is látszik. Ennek megfelelıen a DigitálisÓra osztály definíciójában szereplı hangJelzés() mőveletet is lecseréljük a hangotBekapcsol() és a hangotKikapcsol() mőveletpárra.
56
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
2.13. ábra: Az állapotdiagram egy részletének módosítása DigitálisÓra ... - fókusz: int ... + percÓraJel(): void - frissítés(): void - hangotBekapcsol(): void - hangotBekapcsol(): void - hónapLéptetés(): void - napLéptetés(): void - pontosÓraLéptetés(): void - pontosPercLéptetés(): void - riasztásÓraLéptetés(): void - riasztásPercLéptetés(): void
2.14. ábra: A DigitálisÓra osztály definíciójának módosítása
Ellenırzı/magyarázó kérdések: 1. Az új mőveletek közül miért lettek egyesek publikusak, mások meg privátak?
1. Miért kellett bevezetni a fókusz attribútumot, és miért csak azt?
A DigitálisÓra-példány a percÓraJel() mőveletet más objektum kezdeményezésére hajtja végre, ezért ez publikus. Mivel a kijelzı a frissítés(pKijelzı) mőveletet a digÓra kérésére hajtja végre, ez is publikus. A többi új mővelet mind privát lett, mert ezekre az egyes objektumok önmagukat szólítják fel. Így a frissítés() mőveletre vagy a hangotBekapcsol() mőveletre a percÓraJel() mővelet végrehajtása közben a digÓra szólítja fel önmagát. Ugyancsak ı szólítja fel magát a különféle értékléptetı mőveletekre (pl. napLéptetés()), de már a setFeldolgozás() mővelet végrehajtásának részeként. A DigitálisÓra állapotdiagramja kapcsán fel kell tenni a kérdést: honnan tudja a digitális óra, hogy mikor melyik állapotban van? Más szóval: elegendı attribútummal rendelkezik-e a DigitálisÓra-példány ahhoz, mindegyik állapotát meg tudja különböztetni. Megállapítható, hogy nincs olyan attribútuma a DigitálisÓra osztálynak, amely a Normál mőködés a Hónap állítása, a
DIGITÁLIS ÓRA
57
Nap állítása, … és a Riasztás perc állítása állapotok megkülönböztetésére alkalmas lenne. Célszerő tehát erre bevezetni egy új attribútumot, legyen a neve fókusz. A fókusz értékeinek (egész számok) értelmezése pedig legyen a következı: 0: Normál mőködés állapot 1: Hónap állítása állapot 2: Nap állítása állapot 3: Pontos perc állítása állapot 4: Pontos perc állítása állapot 5: Riasztás óra állítása állapot 6: Riasztás perc állítása állapot A többi állapot megkülönböztetésére már nem kell további új attribútumot bevezetni. Például a Riasztás kikapcsolva és a Riasztás bekapcsolva állapotok megkülönböztethetık a riasztásIgenNem attribútum értékeivel. Az Éppen riaszt és az Éppen nem riaszt állapotok szintén megkülönböztethetık, mégpedig a pontos idı (pontosÓra, pontosPerc) és a riasztási idı (riasztásÓra, riasztásÓra Perc) viszonya alapján. 2.4.4 A állapotdiagram szerepe a programkód kialakításában A 2.11. ábra állapotdiagramja, illetve annak 2.13. ábra szerinti módosítása alapján, már könnyő megadni egy sor mővelet megvalósításának pszeudonyelvő kódját. Elsıként a percÓraJel() mővelet megvalósítását mutatjuk meg. Az állapotdiagramról kiderül, hogy a percÓraJel esemény hatására mindig végre kell hajtani a frissítés() mőveletet; ha azonban speciálisan Éppen riaszt állapotban van a digitális óra, akkor ki kell kapcsolni a hangjelzést is (hangjelzéstKikapcsol() mővelet); ha pedig a frissítéssel jut Éppen riaszt állapotba a digitális óra, akkor be kell kapcsolni a hangjelzést (hangjelzéstBekapcsol() mővelet). OSZTÁLY DigitálisÓra { ... percÓraJel(): void { HA riasztásIgenNem ÉS pontosÓra = riasztásÓra ÉS pontosPerc = riasztásPerc AKKOR hangotKikapcsol(); frissítés(); HA riasztásIgenNem ÉS pontosÓra = riasztásÓra ÉS pontosPerc = riasztásPerc AKKOR hangotBekapcsol(); } }
58
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
Megjegyzés: Az Olvasó joggal kérdezi, hogy iménti példában a HA riasztásIgenNem részletben a riasztásIgenNem helyén a pontosÓra=riasztásÓra feltétel mintájára miért nem a riasztásIgenNem=true feltétel áll? – Éppen állhatna az utóbbi feltétel is, azonban minden feltétel értelme az, hogy tru vagy false értéket ad vissza, ezt pedig a fenti két alak egyformán megteszi. A rövidebb alak esetében a riasztásIgenNem attribútum értéke adódik vissza, amely pontosan akkor true, amikor riasztásIgenNem=true összehasonlítás eredménye true. Ellenırzı/magyarázó kérdések: 1. Mi a magyarázata annak, hogy a percÓraJel() mővelet megvalósításának pszeudokódú leírásában a hang kikapcsolása megelızi a frissítés() mőveletet, a bekapcsolása viszont követi azt?
Vegyünk három tényt: (1) Mint azt már láttuk, a frissítés() mővelet a pontos idıt 1 perccel növeli. (2) A hangotKikapcsol() mőveletet az Éppen riaszt állapot elhagyásakor kell végrehajtani. (3) Ez az állapot (a Riasztás bekapcsolva állapoton belül) arról ismerhetı fel, hogy már a frissítés() végrehajtása elıtt a pontos idı egyenlı a riasztási idıvel. Következmény: Azt, hogy végre kell-e hajtani a hangotKikapcsol() mőveletet, a legkönnyebb a frissítés() mővelet végrehajtása elıtt tesztelni. Most visszatérünk a digÓra és a kijelzı közötti kommunikáció problémájára, pontosabban az erre kínálkozó megoldások közüli választással járó megfontolásokra. Amikor a kijelzı konstruktorára a Kijelzı(pKijelzı: ParaméterKijelzı) szignatúrát alkalmaztuk, illetve a frissítés(pKijelzı: ParaméterKijelzı) szignatúrájú mőveletet bevezettük, amellett döntöttünk, hogy a digÓra és a kijelzı egy pKijelzı paraméterobjektum segítségével fog egymással kommunikálni. Tehát a digÓra a pKijelzı attribútumaiba írja be mindazon kívánságait, amelyek alapján a kijelzı (miután a pKijelzı paramétert megkapta) a kívánt látványt fogja produkálni. Ez a megoldás feltételezi egy ParaméterKijelzı osztály definiálását is, valamint egy asszociációt a DigátálisÓra és a ParaméterKijelzı osztályok között (2.15. ábra). DigitálisÓra
pKijelzı 1
1
ParaméterKijelzı + hónapMezıÉrték: String + hónapMezıLátszik: boolean = true + hónapMezıVillog: boolean = false + napMezıÉrték: String + napMezıLátszik: boolean = true + napMezıVillog: boolean = false + óraMezıÉrték: String + óraMezıLátszik: boolean = true + óraMezıVillog: boolean = false + percMezıÉrték: String + percMezıLátszik: boolean = true + percMezıVillog: boolean = false + alarmMezıÉrték: String = ”ALARM” + alarmMezıLátszik: boolean = false + alarmMezıVillog: boolean = false «constructor» + ParaméterKijelzı()
2.15. ábra: A ParaméterKijelzı osztály definíciója
DIGITÁLIS ÓRA
59
A pKijelzı paraméterobjektum használatának eddig leginkább abban vettük hasznát, hogy vele tömöríthetı volt a specifikáció. Ez jól látható, ha összehasonlítjuk a Kijelzı osztály pszeudokódú definíciójának következı két változatát. // Paraméterobjektumot feltételezı változat OSZTÁLY Kijelzı { ... frissítés(pKijelzı: ParaméterKijelzı): void { // Ide jön a mővelet megvalósítása. } Kijelzı(pKijelzı: ParaméterKijelzı) { // Ide jön a konstruktor megvalósítása. } } // Paraméterobjektum nélküli változat OSZTÁLY Kijelzı { ... frissítés(hónapMezıÉrték: String, hónapMezıVillog: boolean, napMezıÉrték: String, napMezıVillog: boolean, óraMezıÉrték: String, óraMezıVillog: boolean, percMezıÉrték: String, percMezıVillog: boolean, alarmMezıLátszik: boolean, alarmMezıVillog: boolean): void { // Ide jön a mővelet megvalósítása. } Kijelzı(hónapMezıÉrték: String, hónapMezıLátszik: boolean, hónapMezıVillog: boolean, napMezıÉrték: String, napMezıLátszik: boolean, napMezıVillog: boolean, óraMezıÉrték: String, óraMezıLátszik: boolean, óraMezıVillog: boolean, percMezıÉrték: String, percMezıLátszik: boolean, percMezıVillog: boolean, alarmMezıÉrték: String, alarmMezıLátszik: boolean, alarmMezıVillog: boolean) { // Ide jön a konstruktor megvalósítása. } } Megjegyzés: A fenti két változat mellett még egy sor átmeneti változat is elképzelhetı, amelyekkel azonban itt nem foglalkozunk. Tesszük ezt annak ellenére, hogy az átmeneti megoldások között az itt mutatottaknál esetleg elınyösebb megoldások is találhatók. Ugyanakkor arra is fel kell hívni az Olvasó figyelmét, hogy ha a Kijelzı is egy (mások által) elıregyártott osztály, akkor valószínőleg nincs választásunk a digÓra és a kijelzı közötti kommunikáció megoldását illetıen, mert azt az elıregyártott Kijelzı osztály fejlesztıi már rögzítették.
Csupán a fenti két kód összehasonlításából a paraméterobjektum alkalmazása elsöprıen elınyösebbnek tőnik, azonban nem lennénk eléggé körültekintıek, ha más szempontokat nem vennénk figyelembe. Megfontolandó még az tény is, hogy a pKijelzı megközelítıen a kijelzı megduplázása, tehát a program a futása alatt egy kijelzı helyett kvázi kettıt fog
60
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
kezelni, ami futási idıben dupla munkának tőnik. Ráadásul nem csak a futási idıbeni ráfordítások, hanem a programozási idıbeni ráfordítások tekintetében sem egyértelmő a kép. Ennek demonstrálására mindkét változatban pótoljuk a DigitálisÓra() konstruktor megvalósításának a 2.4.1. szakaszban adott pszeudokódú leírásában elhanyagolt azon részletét, amely a kijelzı objektumot hozza létre. // A DigitálisÓra() konstruktor paraméterobjektumot feltételezı változata DigitálisÓra() { ... // Idáig azonos a 2.4.1.szakaszban adott kóddal. // Itt jön a pKijelzı létrehozása és felépítése. // Ez elıkészíti a kijelzı létrehozását. pKijelzı := ParaméterKijelzı(); pKijelzı.hónapMezıÉrték := String(hónap, ”99”); pKijelzı.napMezıÉrték := String(nap, ”99”); pKijelzı.óraMezıÉrték := String(pontosÓra, ”99”); pKijelzı.percMezıÉrték := String(pontosPerc, ”99”); // Itt jön a kijelzı létrehozása. kijelzı:=Kijelzı(pKijelzı); } // A DigitálisÓra() konstruktor paraméterobjektum nélküli változata DigitálisÓra() { hónapMezıÉrték hónapMezıLátszik ... // Idáig azonos a 2.4.1.szakaszban adott kóddal. // Itt jön a kijelzı létrehozása. hónapMezıVillog kijelzı := Kijelzı(String(hónap, ”99”), true, false, String(nap, ”99”), true, false, String(pontosÓra, ”99”), true, false String(pontosPerc, ”99”), true, false ”ALARM”, false, false); }
A DigitálisÓra() konstruktor fenti két megvalósításából látszik, hogy bár a második változatban terjedelmesebb a Kijelzı(…) konstruktor paraméterlistája, azt programozási idıben kompenzálni látszik a pKijelzı felépítésének programozása. 2. Mi a magyarázata annak, hogy a Kijelzı osztály paraméterobjektumot nem használó változatának definícójában a frissítés(…) mővelet paraméterlistája rövidebb, mint a Kijelzı(…) konstruktor paraméterlistája?
3. Mi a magyarázata annak, hogy a DigitálisÓra() konstruktor elsı változata nem látszik értéket adni olyan attribútumoknak, mint pl. a pKijelzı.hónapMezıLátszik vagy
A konstruktornak a kijelzı összes mezıje összes attribútumának kezdıértékét be kell állítani, tehát az összes attribútum paraméterpárjának szerepelni kell a konstruktor paraméterlistájában. A frissítés(…) mőveletnek viszont soha nem kell frissíteni az hónapMezı, a napMezı, az ÓraMezı és a percMezı objektumok látszik attribútumának tartalmát, mert az végig true marad. Hasonlóan soha nem kell frissíteni az alarmMezı objektum érték attribútumát, mert annak tartalma végig ”ALARM” marad. A ParaméterKijelzı osztály 2.15. ábra szerinti definíciója egy sor attribútumhoz (pl. a hónapMezıLátszik attribútumhoz) kezdıértéket is specifikál. Ezeket a kezdıértékeket már a pKijelzı:=ParaméterKijelzı()
DIGITÁLIS ÓRA pKijelzı.hónapMezıVillog? Megjegyzés: Ezt a kérdést a második változattal összevetés is indokolja. Ugyanis az utóbbinál a Kijelzı(…) konstruktorhívás paraméterlistája a hónapMezıLátszik és a hónapMezıVillog paramétereknek megfelelı (true, illetve false) értékeket is tartalmaz.
4. Mi a magyarázata annak, hogy míg általában az attribútumokat privátnak szokás venni, addig a ParaméterKijelzı osztály 2.15. ábra szerinti definíciójában az attribútumok publikusak? 5. Mi a magyarázata annak, hogy a ParaméterKijelzı osztály 2.15. ábra szerinti definíciójában nincs a konstruktoron kívül más (az attribútumokat író vagy olvasó) mővelet?
61
konstruktorfuttatás beállítja. Tehát azt követıen már csak azon attribútumok értékeit kell beállítani, amelyekhez a 2.15. ábrán nem tartozik kezdıérték.
Ez teszi lehetıvé, hogy a digÓra közvetlenül elérje pKijelzı attribútumait (úgy, mint a DigitálisÓra() konstruktor pKijelzıt használó változatának pszeudokódjában). Nincs rájuk szükség, ha más objektumok (a digÓra és a kijelzı) közvetlenül érik el a pKijelzı attribútumait.
Ha a két megoldást nem a Kijelzı(…) konstruktor használata, hanem a Kijelzı osztály frissítés(…) mőveletének használata szempontjából hasonlítjuk össze, akkor mégis a pKijelzı használata felé hajlik a mérleg. A DigitálisÓra osztály frissítés() mőveletének a pszeudokódjában (lásd a 2.11. ábra elıtt) is ezt a megoldást feltételeztük, és az ott határozottan kényelmesnek bizonyult: Mivel a digÓra közvetlenül értéket tud adni a pKijelzı attribútumainak, elegendı mindig csak annak az attribútumnak az értékét megváltoztatnia, amelyet az aktuális frissítés(…) éppen érint (például, az ALARM ON/OFF gombra kattintáskor csak az alarmMezı láthatóságát kell ellenkezıjére változtatni); nem kell mindig tíz paraméterrel foglalkoznia. 6. Ha azzal érvelünk a pKijelzı használata mellett, hogy annak attribútumait a digÓra közvetlenül elérheti, akkor nem lenne még jobb megoldás, ha a digÓra a kijelzı mezıinek attribútumait érné el közvetlenül? (Akkor a pKijelzıre sem lenne szükség.)
Az ilyen megoldás ellen több érv szól: a) Ha a Kijelzı egy, mások által elıregyártott osztály, akkor esetleg meg sem engedi, hogy a külvilág elérje az alkatrészei attribútumait, így akkor szóba sem jöhet a felvetett ötlet. b) Ha a Kijelzı mégis megengedi az alkatrészei attribútumainak közvetlen elérését (más osztályok példányai számára), az ezt kihasználó megoldás a késıbbi változtatások miatt aggályos: Ha le kell cserélni a kijelzıt egy más konstrukciójú kijelzıre, akkor a digitális óra kódját is át kell írni. (Persze a digitális óránk meglehetısen egyszerő szerkezet, így annak újraprogramozása sem jelentene komoly plusz munkát, de itt most olyan elvekhez próbáljuk tartani magunkat, amelyek bonyolult, nagy rendszerek esetében is mőködnek.)
Annak megmutatására, hogy milyen hasznos lehet az állapotdiagram a fejlesztık számára, itt következik a selectFeldolgozás(), a setFeldolgozás() és az alarmOnOff-
62
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
Feldolgozás() mőveletek – állapotdiagramból kiolvasható – megvalósításának pszeudokódú leírása. OSZTÁLY DigitálisÓra { ... selectFeldolgozás(): void { fókusz:=fókusz+1; HA fókusz > 6 AKKOR fókusz:=0; HA fókusz = 0 AKKOR { // Normál mőködés állapotba lépés pKijelzı.óraMezıÉrték := String(pontosÓra,”99”); pKijelzı.percMezıÉrték := String(pontosPerc,”99”); pKijelzı.percMezıVillog := false; // Utoljára ez lehetett true. pKijelzı.alarmMezıLátszik := riasztásIgenNem; } KÜLÖNBEN HA fókusz = 1 AKKOR { // Hónap állítása állapotba lépés
HA riasztásIgenNem ÉS pontosÓra = riasztásÓra ÉS pontosPerc = riasztásPerc AKKOR hangotKikapcsol(); pKijelzı.hónapMezıVillog := true; } KÜLÖNBEN HA fókusz = 2 AKKOR { // Nap állítása állapotba lépés pKijelzı.hónapMezıVillog := false; pKijelzı.napMezıVillog := true; } KÜLÖNBEN HA fókusz = 3 AKKOR { // Pontos óra állítása állapotba lépés pKijelzı.napMezıVillog := false; pKijelzı.óraMezıVillog := true; } KÜLÖNBEN HA fókusz = 4 AKKOR { // Pontos perc állítása állapotba lépés pKijelzı.óraMezıVillog := false; pKijelzı.percMezıVillog := true; } KÜLÖNBEN HA fókusz = 5 AKKOR { // Riasztás óra állítása állapotba lépés pKijelzı.óraMezıÉrték := String(riasztásÓra,”99”); pKijelzı.percMezıÉrték := String(riasztásPerc,”99”); pKijelzı.alarmMezıLátszik := true; pKijelzı.percMezıVillog := false; pKijelzı.óraMezıVillog := true; pKijelzı.alarmMezıVillog := true; } KÜLÖNBEN HA fókusz = 6 AKKOR { // Riasztás perc állítása állapotba lépés pKijelzı.óraMezıVillog := false; pKijelzı.percMezıVillog := true; } kijelzı.frissítés(pKijelzı); } setFeldolgozás(): void { HA fokusz = 1 AKKOR hónapLéptetés(); KÜLÖNBEN HA fokusz = 2 AKKOR napLéptetés(); KÜLÖNBEN HA fokusz = 3 AKKOR pontosÓraLéptetés(); KÜLÖNBEN HA fokusz = 4 AKKOR pontosPercLéptetés(); KÜLÖNBEN HA fokusz = 5 AKKOR riasztásÓraLéptetés(); KÜLÖNBEN HA fokusz = 6 AKKOR riasztásPercLéptetés(); }
DIGITÁLIS ÓRA
63
PRIVÁT hónapLéptetés(): void { hónap := hónap + 1; HA hónap => 12 AKKOR hónap := 1; pKijelzı.hónapMezıÉrték := String(hónap,”99”); kijelzı.frissítés(pKijelzı); } PRIVÁT napLéptetés(): void { nap := hónap + 1; HA nap > maxNap(hónap) AKKOR nap := 1; pKijelzı.napMezıÉrték := String(nap,”99”); kijelzı.frissítés(pKijelzı); } PRIVÁT pontosÓraLéptetés(): void { pontosÓra := pontosÓra + 1; HA pontosÓra > 23 AKKOR pontosÓra:= 0; pKijelzı.óraMezıÉrték := String(pontosÓra,”99”); kijelzı.frissítés(pKijelzı); } PRIVÁT pontosPercLéptetés(): void { pontosPerc := pontosPerc + 1; HA pontosPerc > 59 AKKOR pontosPerc:= 0; pKijelzı.percMezıÉrték := String(pontosPerc,”99”); kijelzı.frissítés(pKijelzı); } PRIVÁT riasztásÓraLéptetés(): void { riasztásÓra := riasztásÓra + 1; HA riasztásÓra => 23 AKKOR riasztásÓra:= 0; pKijelzı.óraMezıÉrték := String(riasztásÓra,”99”); kijelzı.frissítés(pKijelzı); } PRIVÁT riasztásPercLéptetés(): void { riasztásPerc := riasztásPerc + 1; HA riasztásPerc => 59 AKKOR riasztásPerc:= 0; pKijelzı.percMezıÉrték := String(riasztásPerc,”99”); kijelzı.frissítés(pKijelzı); } alarmOnOffFeldolgozás(): void {
HA riasztásIgenNem ÉS pontosÓra = riasztásÓra ÉS pontosPerc = riasztásPerc AKKOR hangotKikapcsol(); riasztásIgeNem := NEM(riasztásIgenNem); pKijelzı.alarmMezıLátszik := riasztásIgeNem; kijelzı.frissítés(pKijelzı); } }
Mivel a digÓra objektum az egész szerkezet agya, az állapotdiagramja lényegében a szerkezet minden viselkedési jellemzıjének leírására alkalmas; így (ha idáig alapos munkát végeztünk) a további dinamikus modellek nem fognak új összefüggéseket feltárni. Annak, hogy itt mégis következik két szekvenciadiagram, már nem új ismeretek közlése a célja, hanem az eddig közöltek jobb megértetése: Amit eddig leírtunk állapotdiagrammal, aztán a DigitálisÓra osztály frissítés() mőveletének pszeudokódjában (a 2.4.2. szakaszban) vagy ugyanezen osztály percÓraJel(), selectFeldolgozás(), setFeldol-
64
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
gozás() mőveleteinek és az utóbbiból hívott léptetı mőveleteknek, valamint az alarmOnOffFeldolgozás() mőveletnek a pszeudokódjában (a 2.4.4. szakaszban), most bemutatjuk – más oldalról – szekvenciadiagramokkal is. 2.4.5 Szekvenciadiagram az óra normál mőködése alatti egyik folyamatról Az idáig szerzett ismereteket felhasználva most egy szekvenciadiagrammal írjuk le az érintett objektumok együttmőködését abban a folyamatban, amely úgy indul, hogy a digÓra objektum a Riasztás bekapcsolva állapotban van, továbbá: pontosÓra:5, pontosPerc:59, riasztásÓra:6, riasztásPerc:0. Ezt követıen kétszer bekövetkezik a percÓraJel esemény, míg a felhasználó egyszer sem avatkozik bele a folyamatba. Elıször gondoljuk át, mik lesznek a példaobjektumok!: • digÓra: Ez egyértelmően következik a feladat leírásából. • Operációs rendszer: Ez egy külsı objektum, amelytıl a percÓraJel esemény érkezik. • kijelzı: Az idı változásának a kijelzın is látszani kell. • óraMezı: A kijelzınek ez az egyik mezıje, amely a változásokban effektíven érintett. • percMezı: A kijelzınek ez a másik mezıje, amely a változásokban effektíven érintett. A fenti felsorolás ellenére a diagram elsı változatán (2.16. ábra) csak két objektum szerepel. Ennek az az oka, hogy ebben a változatban a frissítés() öndelegáció magába rejti azokat a részleteket, amelyek szükségessé tennék a további példaobjektumok szerepeltetését is. 1. megjegyzés: Ez a diagram a percÓraJel() mővelet pszeudokódja alapján készült. 2. megjegyzés: A diagramon a pontosIdı = riasztásIdı feltétel rövidítése a pontosÓra = riasztásÓra ÉS pontosPerc = riasztásPerc összetett feltételnek.
2.16. ábra: A szekvenciadiagram elsı változata
DIGITÁLIS ÓRA
65
A diagram következı változatában (2.17. ábra) a frissítés() helyett már annak részletezı kifejtését ábrázoljuk, így azon már nincsenek rejtve azok a mőveletek, amelyek a kijelzı, az óraMezı és a percMezı példaobjektumokat is érintik.
2.17. ábra: A szekvenciadiagram második változata
Megjegyzések a 2.17. ábrához: •
Ez a diagram a DigitálisÓra osztály frissítés() mővelete pszeudokódjával (lásd a 2.4.2. szakaszban) összhangban készült.
66
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK •
•
A bekeretezett részekben a diagram szerkesztıje csak azokat a mőveleteket tüntette fel, amelyek tényleges hatást váltanak ki a kijelzınél. E részekben álló három pont (…) jelzi, hogy a szerkesztı itt valamilyen mozzanatokat elhanyagolt. Hogy mik ezek a mozzanatok, az a frissítés(pKijelzı) mővelet megvalósításától függ. A következı pszeudokód az egyik lehetséges (bár nem túl intelligens) megvalósítást mutatja. Eszerint a kijelzı a pKijelzı paraméterobjektum mind a 15 attribútumának megfelelıen intéz egy-egy kérést a megfelelı mezıhöz – akár változott az attribútum értéke, akár nem. Azonban a mezıknél effektív változást csak azok a kérések váltanak ki, amely a korábbiakhoz képes eltérı paraméterértéket közölnek. A diagram csak az ilyen kéréseket tartalmazza, tehát az elsı keretben az //1 és //2 jelő kiemelt kéréseket, a második keretben pedig csak a // 2 jelő kiemelt kérést. A diagramon néhány öndelegáció szimbólum ténylegesen nem öndelegációt jelöl, csak azért alkalmaztuk ıket, hogy az adott helyen részletezhessük, hogyan reagál az üzenetre az éppen megszólított objektum.
// Paraméterobjektumot feltételezı változat OSZTÁLY Kijelzı { ... frissítés(pKijelzı: ParaméterKijelzı): void { // Egy lehetséges (de nem túl intelligens) megvalósítás hónapMezı.setÉrték(pKijelzı.hónapMezıÉrték); hónapMezı.setLátszik(pKijelzı.hónapMezıLátszik); hónapMezı.setVillog(pKijelzı.hónapMezıVillog); napMezı.setÉrték(pKijelzı.napMezıÉrték); napMezı.setLátszik(pKijelzı.napMezıLátszik); napMezı.setVillog(pKijelzı.napMezıVillog);
óraMezı.setÉrték(pKijelzı.óraMezıÉrték); //1 óraMezı.setLátszik(pKijelzı.óraMezıLátszik); óraMezı.setVillog(pKijelzı.óraMezıVillog);
percMezı.setÉrték(pKijelzı.percMezıÉrték); //2 percMezı.setLátszik(pKijelzı.percMezıLátszik); percMezı.setVillog(pKijelzı.percMezıVillog); alarmMezı.setÉrték(pKijelzı.alarmMezıÉrték); alarmMezı.setLátszik(pKijelzı.alarmMezıLátszik); alarmMezı.setVillog(pKijelzı.alarmMezıVillog); } ... }
Ellenırzı/magyarázó kérdések: 1. A diagramról kiderül, hogy a folyamat a pKijelzı objektumot is használja. Ha így van, akkor miért nem szerepel egy pKijelzı példaobjektum a diagramon?
2. Mennyiben változna a szekvenciadiagram, ha a feladat az eredetitıl csak abban különbözne, hogy a két percÓraJel esemény között a felhasználó az ALARM ON / OFF gombra kattintana?
A diagramon az egymással üzeneteket váltó objektumokat kell feltüntetni. A digÓra nem üzenetek útján használja a pKijelzı objektumot, hanem közvetlenül eléri annak attribútumait, közvetlenül írja / változtatja az attribútumainak értékeit. Az Olvasó önállóan válaszoljon a kérdésre!
DIGITÁLIS ÓRA 3. Mennyiben változna a szekvenciadiagram, ha a feladat az eredetitıl csak abban különbözne, hogy a folyamat elején a pontos idı: hónap:4, nap:30, pontosÓra:23, pontosPerc:59 lenne?
67
Az Olvasó önállóan válaszoljon a kérdésre!
2.4.6 Szekvenciadiagram az óra beállítása alatti egyik folyamatról Szekvenciadiagrammal leírjuk az érintett objektumok együttmőködését abban a folyamatban, amelynek kezdetén a digÓra a Pontos óra állítása állapotban van, majd ezt követıen a felhasználó kétszer a SELECT gombra, majd kétszer a SET gombra kattint. A folyamat elején pontosÓra:11, pontosPerc:3, riasztásÓra:22, riasztásPerc:30. – A diagramot a statikus modell legutolsó változatával összhangban rajzoljuk meg. Most is a példaobjektumok meghatározásával kezdjük: • Felhasználó: Külsı objektum (aktor). • digÓra: Ez egyértelmően következik a feladat leírásából. • select, set: A felhasználó által használt gombok. • selectF, setF: A gombeseményeket a digÓra objektum felé közvetítı eseményfigyelık. • kijelzı: A felhasználó által végzett mőveletek eredményét ennek mezıi mutatják, a mezıket pedig a digÓra csak a kijelzı közvetítésével éri el. • óraMezı, percMezı, alarmMezı: A kijelzı három olyan mezıje, amelyet a változások érintenek.
2.18. ábra: A szekvenciadiagram elsı (áttekintı) változata
68
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
selectFeldolgozás()
selectFeldolgozás()
riasztásÓraLéptetés() kifejtve
riasztásÓraLéptetés() kifejtés nélkül 2.19. ábra: A szekvenciadiagram második (részletezı) változata
DIGITÁLIS ÓRA
69
A 2.18. ábra a diagram egy áttekintı változatát mutatja. Mivel ezen a selectFeldolgozás() és a setFeldolgozás() mőveletek nincsenek kibontva, ezek rejtik azokat a részleteket, amelyek a kijelzıt és annak mezıit érintik, így azok ezen a diagramon még nem szerepelnek. Az említett mőveletek a 2.19. ábrán látható diagramváltozat fejti ki. Megjegyzés: A 2.17. ábrához hasonlóan itt is igaz, hogy a kijelzı és a mezıi közötti kommunikációban csak azokat a mőveleteket tüntette fel a diagram szerkesztıje, amelyek tényleges változást idéznek elı a kijelzı mezıinél, de a hiányzó (hatástalan) részeknek három ponttal (…) való jelzését a szerkesztı itt már hanyagolta. – Másik hasonlóság, hogy itt is vannak olyan öndelegáció szimbólumok, amelyek ténylegesen nem öndelegációt takarnak, csupán az érintett objektum aktivitásának részletezésére adnak formai lehetıséget. Ellenırzı/magyarázó kérdések (ide az Olvasó kérdései jöhetnek):
70
OBJEKTUMORIENTÁLT TERVEZÉS – ESETTANULMÁNYOK
2.5 Egy távolabbi analógia Ha az Olvasó szándéka történetesen egy vállalatirányítási alkalmazás fejlesztésében hasznosítható ismeretekre szert tenni, akkor femerülhet benne az a kérdés, hogy a számára érdekes téma irányába mennyiben viheti ıt elıre a digitális óra OO modellezésének gyakorlása, mert elsı látásra valószínőleg nem tőnik úgy, hogy a két dolognak köze lenne egymáshoz. Nos, azt feltehetıen nem kell sokáig bizonygatni, hogy az OO technológia gyakorlati megismerését valamilyen egyszerőbb feladat megoldásával kell kezdeni, és egy vállalatirányítási rendszer nem ilyen, de még annak kisebb moduljai sem. Azonban ettıl több indok is található arra, hogy itt a digitális óra téma lehetett. A vállalatirányítási alkalmazás és a digitális óra között felfedezhetı néhány – bár távoli, de mégis lényegi – analógia, amelynek elemeit a következı táblázat foglalja össze: Feldolgozott események (tranzakciók)
Digitális óra Pontos idı és riasztási idı beállítása, riasztás ki-/bekapcsolása, percórajel, riasztási idıpont bekövetkezése.
Kimenetek
Pontos idı mutatása, riasztás
Felhasználói felület Alkalmazáslogika
Kijelzı és a gombok A digÓra vezérlési szabályai
Vállalatirányítási alkalmazás Törzsadatok karbantartása, gazdasági és logisztikai eseményeket tartalmazó bizonylatok rögzítése, fizetés esedékességének bekövetkezése, adatszolgáltatási (bevallási) határidık lejárta. Állapotadatok (készletek, számlaegyenlegek) megjelenítése, idızített kimutatások, bevallások, eredménykimutatás, mérleg, … Menők, képernyıőrlapok, … Az alkalmazással érvényesített üzleti szabályok.