Tartalmi kivonat
Objektumorientált nyelvek és módszertanok Előadás vázlat Szerzők: Beszédes Árpád és Ferenc Rudolf Készült a Sysdata Kft. 2001/2002-es oktatói ösztöndíjának támogatásával 2002.0304 2002 Tartalomjegyzék: 1 BEVEZETÉS . 5 2 OBJEKTUMORIENTÁLT ANALÍZIS ÉS DESIGN . 7 2.1 2.2 2.3 2.4 2.5 3 OBJEKTUMKOMPONENS TECHNOLÓGIÁK . 65 3.1 3.2 4 BEVEZETÉS . 7 VIZUÁLIS MODELLEZÉS . 8 A FOLYAMAT. 10 UNIFIED MODELING LANGUAGE . 16 A „KURZUSFELVÉTEL” PÉLDAPROJEKT . 37 BEVEZETÉS . 65 CORBA . 66 OBJEKTUMORIENTÁLT NYELVEK . 71 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 4.13 4.14 4.15 BEVEZETÉS . 71 ABSZTRAKCIÓ . 72 OOP JELLEMZŐI . 72 JAVA BEVEZETÉS . 75 JAVA ÉS AZ INTERNET . 77 JAVA ALKALMAZÁSOK . 78 MINDEN OBJEKTUM . 79 PROGRAMFUTÁS VEZÉRLÉS . 83 IMPLEMENTÁCIÓ ELREJTÉSE . 88 OSZTÁLYOK ÚJRAFELHASZNÁLÁSA . 91 POLIMORFIZMUS . 94 INTERFÉSZEK ÉS BELSŐ OSZTÁLYOK . 96 OBJEKTUMOK TÁROLÁSA: KOLLEKCIÓK (KONTÉNEREK) . 98
HIBAKEZELÉS KIVÉTELEKKEL . 104 EGYÉB JAVA TECHNOLÓGIÁK . 106 1 1 Bevezetés 5 Bevezetés Az Objektumorientált (OO) programozás nem új keletű. A hagyományos szemlélet szerint az objektumorientáltság több mint egyszerűen osztályokkal és objektumokkal bővített procedurális nyelv; új szemléletmódra van szükség. Ez a szemléletmód az Objektumorientált Paradigma (OOP). Számos megfogalmazás született arra, hogy mi képezi az alapját az OOP-nek. Leginkább az alábbi három tulajdonságot tekintik ennek a szemléletmódnak az alapját: öröklődés, egységbezárás és többalakúság (polimorfizmus). Napjainkban már az ilyen jellegű megközelítés már nem tekinthető teljesnek. Valójában, nagyon nagy területet ölel fel az objektumorientáltság fogalma: számos OO programnyelv létezik, szoftverfejlesztési módszertanok vannak, és egyéb technológiák is. Jelen kurzus egy szűk keresztmetszetét nyújthatja csak ennek a témának, a
hangsúly néhány modern technológián lesz. Az első részben az objektumorientált analízis és tervezés (A/D) alapjaival ismerkedhet meg az olvasó, különös tekintettel a vizuális modellezés fontosságára, konkrét módszertan (RUP), illetve modellező nyelv (UML) bemutatásával. A második rész témája az objektumkomponens technológiák rövid ismertetése, a CORBA részletesebb bemutatásával. A kurzus záró részében az objektumorientált nyelvek tulajdonságaival foglalkozunk, a Java nyelv bemutatásán keresztül. A fenti három téma közötti kapcsolatok, és egymásra való épülésük is bemutatásra kerülnek annak érdekében, hogy az olvasó elsajátíthassa azt, hogy hogyan lehet valós méretű objektumorientált szoftverrendszereket hatékonyan elkészíteni. Mindazonáltal, a hagyományos értelemben vett OO programozási technikákkal, mint pl. az öröklődés és a polimorfizmus helyes használata, nem foglalkozunk! Ezekre számos jó anyag
jelent meg az elmúlt években. Természetesen, ez a képesség számos egyéb területtől is függ: pl. a procedurális programozás és a hagyományos algoritmusok és adatszerkezetek ismeretei továbbra is fontosak, illetve néhány egyéb újabb programozás- és programfejlesztési technika is nélkülözhetetlen lehet, mint pl. a generikus programozás Az alábbi ábrán láthatjuk, hogyan épülnek az OO szoftverfejlesztési módszertanok a klasszikus OOP elemeire, valamint azt, hogy egy szoftverfejlesztési ciklus analízis és tervezési (design) tevékenységei ezeket a módszertanokat alkalmazzák. Így jutunk el a modellezés fogalmához, amely alapvető módjának bizonyult a hatékony objektumorientált szoftverfejlesztésnek. Ezen témákkal indítjuk a következő fejezetet. Analízis és Design modellezés OO módszertanok magasabb szintű (vizuális) nyelvek, technológiák, folyamatok Klasszikus OO paradigma programozási nyelvek, technológiák
Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 2 Objektumorientált analízis és design 7 2 Objektumorientált analízis és design 2.1 Bevezetés Egy teljes szoftverfejlesztési ciklus során elvégzendő tevékenységek általában különböző jellegűek, pl. a kezdeti fázisokban általában magasabb absztrakciókban gondolkodunk, míg a fejlesztési ciklus végére konkrét implementáció szint ű problémákat kell megoldani. Objektumorientált rendszerek fejlesztésére különösképpen jellemző az alábbi két típusú gondolkodás és tevékenység: • Analízis szintű gondolkodás. Egy szoftver fejlesztésének korai fázisaiban a megvalósítandó rendszer feladatait szeretnénk feltérképezni, azok funkcionális és egyéb jellegű követelményeit. Más szóval, a kérdés ilyenkor az, hogy a rendszernek mit kellene tennie. Ilyenkor határozzuk meg a szoftver (formális és informális) specifikációját,
majd abból kiindulva kezdjük magasabb szintű absztrakciók segítségével előállítani a rendszer modelljét, amely a konkrét megvalósítás alapját fogja képezni. • Tervezés (design) szintű gondolkodás. A meglévő (és folyamatosan fejlődő) modell alapján a szoftver konkrét implementációjához vezet ő utat tervezzük meg. Ilyenkor már arra keressük a választ, hogy a meghatározott specifikációt hogyan valósítsa meg a rendszer. Ezen a szinten már képbe kerülnek különböző alacsonyabb szintű technológiák is, mint pl. a kommunikációs protokollok és programozási nyelvek és technológiák. Az analízis és tervezési tevékenységek hatékony és célszerű sorrendjét és módját különböző módszerekkel és folyamatokkal kell vezérelni. Az alábbiakban megadunk néhány alapfogalom általánosan használt értelmezését. • Folyamat (Process). A fejlesztési életciklust irányítja, meghatározza annak lépéseit és a lépések
sorrendjét. • Jelölésrendszer (Notation). A jelölésrendszer szintaktikai és szemantikai szabályok összessége (más szóval egy nyelv), amely a fejlesztési ciklus során alkalmazható a terv elkészítéséhez vizuális eszközök segítségével. • Módszer (Method). Általában egy folyamat és a hozzátartozó jelölésrendszer együttesét módszernek szokás nevezni. Általában egy jól definiált területen belül alkalmazható. • Módszertan (Methodology). Nagyobb területet fed le, és sok esetben független az alkalmazás területétől. Valójában módszerek csoportja Módszerekből (és módszertanokból) számos fajta létezik. Így megkülönböztetünk strukturált, adatvezérelt, objektumorientált, stb. módszereket Nem ritkán CASE eszközök definiálják, illetve használják saját módszertanaikat, azonban az utóbbi időben egyre jelentősebb az a törekvés, hogy szabványos módszerek, módszertanok legyenek széleskörűen használva. Az
alábbiakban néhány példát sorolunk fel ismertebb módszerekre és módszertanokra: • SA/SD módszer, Jackson (JSD) módszer Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 8 2.11 2.2 Vizuális modellezés • SSADM módszertan (információs rendszereknél alkalmazandó) • Metis Design módszertan (OO információs rendszerek) • OMT (UML egyik elődje) Hivatkozások Terry Quatrani: Visual Modeling With Rational Rose and UML • Addison-Wesley, 1998 Martin Fowler: UML Distilled Second Edition • Addison-Wesley, 2000 OMG Unified Modeling Language Specification, Version 1.4 • Objecet Management Group, 2001 Cris Kobryn, et al.: Object Modeling with OMG UML Tutorial Series • OMG, 1999-2001 Gary K. Evans: A Simplified Approach to RUP (fordította Győrbíró Norbert, 2002) • http://www.therationaledgecom/content/jan 01/t rup gehtml Web címek: • http://www.rationalcom/ • http://www.omgcom/ •
http://www.umlorg/ • http://www.rationalcom/products/rup/indexjsp 2.2 Vizuális modellezés 2.21 Miért modellezünk? Hogyan lehet hatékonyan elkészíteni egy nagy méretű szoftverrendszert? Ahhoz, hogy ez a cél kezelhető legyen emberi mértékű befogadóképességgel, a megvalósítandó rendszert kisebb és a célnak megfelelő módon kell ábrázolnunk. Valójában ilyenkor a rendszer egy modelljét vizsgáljuk, és azon dolgozunk a végső megvalósításig. A modell egy adott probléma leírása a valós világból vett ötletekkel, ami azt jelenti, hogy a probléma kezelését a már megszokott problémamegoldó képességeinkre bízzuk annak érdekében, hogy azt minél hatékonyabban (ösztönszerűen) tegyük. Úgy is mondhatnánk, hogy amíg a szoftver teljes és abszolút specifikációja a számítógép számára maga a forráskód, addig nekünk embereknek egy könnyebben emészthető formában, a modellben tálaljuk. Modellezéskor mindig a rendszer
lényeges részeit vizsgálhatjuk, azokat amelyek a részfeladat megoldáshoz éppen a legalkalmasabbak. Ezzel egyidejűleg a skálázhatóság problémáját is megoldjuk, ugyanis egy való méret ű szoftverrendszert (gondoljuk itt csak egy teljes banki ügyviteli-, vagy egy reptéri 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 9 irányító szoftverre) kisebb, ember által is feldolgozható részekre (nézetekre) bontjuk. Ezzel a módszerrel gyakorlatilag tetszőleges komplexitású rendszer modellezhető. A szoftverfejlesztést mint modellezés akkor áll leginkább közelebb az emberi felfogóképességeinkhez, amikor vizuális eszközöket használunk, azaz szabványos grafikai eszközökkel végezzük a modellezést. Egy találó hasonlat szerint „1 bitmap = 1 megaword”, tehát ábrák segítségével sokszor könnyebben megérthető számunkra egy-egy probléma vagy megoldás.
Szokás a szoftver analízis és tervezés (modellezés) folyamatait sokkal inkább hasonlítani a mérnöki tudományokhoz, mint matematikai megközelítéshez. A mérnökök által használt tervezési módszerek sikeresen alkalmazhatók „szoftvermérnökök” esetében is. Jó példa erre az építészet Egy felhőkarcolót építési tervdokumentáció nélkül képtelenség elkezdeni, míg egy kisebb szerszámtárolóhoz sokkal szerényebb terv is elegendő. Így van ez a szoftver esetében is: kis „példaprogramohoz” az ember nyilván nem készít tervet, viszont nagy méretű és bonyolultságú rendszerhez annál inkább. Mint ahogy az építészetben is több különböző szempont szerint kell a terveket elkészíteni (statikai, elektromos, ), a szoftvertervezésnél is több „nézet” segítségével írjuk le a rendszert (szerkezeti, viselkedési, használati eset). A fentieken kívül még számos előnye van a vizuális modellezésnek, pl.: 2.22 •
Esettanulmányok a felhasználó szempontjából. Ez azt jelenti, hogy egy nem szoftver szakember is könnyen megértheti bizonyos részeit a modellnek. • Üzleti folyamatok leírására is alkalmas (forráskód mentesen). • Jó kommunikációs eszközt képez a szoftver fejlesztésében résztvevő szereplők között: mérnökök, kódolók, tesztelők, menedzserek, a megrendelő, jogászok, • Fejlesztési idő és rizikó csökkentése, amely nagy projektek esetében nem elhanyagolható (anyagi) szempont. • Szoftver architektúra definiálása. A szoftver felépítését több szemszögből lehet megjeleníteni. • Szoftver újrafelhasználhatóság. Az objektumorientáltság hagyományos előnyein kívül (öröklődés, egységbezárás), itt még jobban jellemző az újrafelhasználhatóság, csak nagyobb méretekben (szoftverkomponensek). A „siker háromszöge” Ahhoz hogy a szoftverfejlesztési ciklus sikeres legyen az alábbi három dolog
nélkülözhetetlen: • Folyamat. A folyamat irányítja a szoftverfejlesztési ciklust, definiálja annak lépéseit és a lépések sorrendjét. • Jelölésrendszer. Szabványos jelölésrendszer, amely leírja hogyan kell hogy kinézzen a tervezési dokumentáció. • Eszköz. Szoftver, amellyel mindez elvégezhető Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 10 2.3 A folyamat Jelölésrendszer (Notation) Notation) Folyamat (Process) Process) Eszköz (Tool) Tool) A fenti ábra azt sugallja, hogy ha a három elem közül valamelyik is hiányzik, akkor a hatékony szoftverfejlesztés nem lehetséges: folyamat nélkül kaotikus a projekt, szabványos jelölésrendszer nélkül nincs jó kommunikáció, eszközre viszont mindig szükség van, ha más nem, legyen az papír és ceruza. A következő részben a folyamattal foglalkozunk (RUP), utána pedig a jelölésrendszer következik (UML). 2.3 A folyamat
2.31 Rational Unified Process A siker háromszögének első és legfontosabb eleme a folyamat, hiszen az irányítja az egész szoftverfejlesztési életciklust. Leírja a végrehajtandó lépéseket és a végrehajtás sorrendjét. Valójában az ésszerű szoftverfejlesztés ésszerű módját írja le, amelyet sok gyakorlattal mi is elsajátíthatunk, de ebben segít a formális leírás. Két legfontosabb jellemzője, miszerint az előírt fejlesztési életciklus: • Iteratív. Ez azt jelenti, hogy a szoftverfejlesztés kisebb mértékű „alprojektekre” van bontva, és ezek iteratívan követik egymást. • Inkrementális. Az inkrementalitás azt jelenti, hogy minden iteráció után a szoftver egyre „jobb”, tehát a fejlesztés fokozatos. A lényeg az, hogy az egyes iterációkra jobban lehet összpontosítani és jól definiált feladatokat kell elvégezni. Egyik alapvet ő irányelv az, hogy minden pillanatban jól tudjuk, hogy mi a célunk és merre halad a
projektünk, tehát azt próbáljuk elkerülni, hogy a szoftver fejlesztése közben a végcél is változzon (ez megfelelő folyamat hiányában elkerülhetetlen és nagyon nem hatékony fejlesztéshez vezet). Ha egy iteráció során valami probléma merül fel, akkor könnyebb a problémát kezelni, mert csak azon az alprojekten belül kell vizsgálódni. Minden iterációnak az a célja, hogy a részfeladat megoldásával közelebb kerüljünk a szoftver specifikált működéséhez. Minden iterációban az alábbi fő tevékenységeket kell elvégezni: • követelmények gyűjtése az adott iteráció végével kapcsolatban • analízis: a feladatok pontos kitűzése • tervezés: a megoldás megtervezése • implementáció: a terv megvalósítása (pl. kódolás) 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design • 11 tesztelés: az adott iteráció követelményei
megoldottak-e? Ha a teszteléskor kiderül, hogy valami nincs rendben, akkor vissza lehet menni az iteráció elejére és megismételni azt. Egy-egy iteráció általában az egyik funkcionalitás megvalósítására vonatkozik, vagy valamely már meglévő funkcionalitás javítása a feladat. Ha az iteráció végrehajtása sikeres, akkor mondhatjuk, hogy egy részét megoldottuk a problémának vagy egy rizikót csökkentettünk (pl. hibát javítottunk) Az inkrementalitás tehát valójában a rizikó folyamatos csillapítását jelenti, mint ahogy azt az alábbi ábra is jól mutatja: A fent vázolt folyamat valójában a Rational Unified Process (RUP) alapfilozófiáját követi. A RUP egy olyan egységes folyamat leírás, amely több már meglévő és jól működő folyamat egységesítésével jött létre, azok legjobb tulajdonságait átvéve (régebbi elnevezése a Rational Objectory Process). A RUP nem más, mint irányelvek gyűjteménye, egy természetes nyelven
íródott specifikáció. A szoftverfejlesztés különböző technikai és szervezési kérdéseivel foglalkozik, és betartásával a fejlesztési ciklus a lehető leghatékonyabban és költségkímélő módon végezhető el. A folyamat leírása két dimenzióba szerveződik: • Idő. Egyrészt definiálja az iterációkat, és a főbb fázisokat a tervezés során • Fejlesztési elemek. Az időciklus során elvégzendő feladatok és aktivitások vannak leírva. Az alábbi ábrán a fenti szerveződést láthatjuk: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 12 2.3 A folyamat Négy fő fázisa van a fejlesztési ciklusnak: kezdeti, kidolgozás, megvalósítás és átadás. Az első és az utolsó fázisok nem szigorúan a fejlesztés részei, hanem kiegészítő szakaszok. Az iterációk száma nincs rögzítve, az a konkrét feladattól függ. A kezdeti fázisokban különösen fontos az egyes iterációk
megtervezése A fejlesztési elemek két kategóriába vannak osztva: valódi technikai folyamat komponensek (követelmények gyűjtése, analízis és tervezés, implementáció és tesztelés) és a kiegészítő szervezési komponensek (menedzsment, környezeti elemek és telepítés). Az ábrából világosan látható az egyes elemek intenzitásának váltakozása az időciklus folyamán. Pl a tesztelés iterációnként jellemző, de a végső fázisokban nyilván a legjelentősebb. Maga a folyamat leírása nagy és szerteágazó annak érdekében, hogy a lehet ő legszélesebb körben alkalmazható legyen: különböző méretű és jellegű rendszerek tervezésére is alkalmas. Ez sajnos azt is maga után vonja, hogy viszonylag hosszú időre van szükség ahhoz, hogy elsajátítható legyen (bár, hosszútávon ez csakis jó befektetés lehet!). Az alábbiakban ismertetjük a RUP legszükségesebb lépéseit, amely ismerettel már a legtöbb esetben alkalmazható a módszer
és ugyanakkor jó alapot is nyújt a teljes specifikáció elsajátításához [Evans nyomán]. Egyúttal eloszlatjuk a RUP méretéből adódó alkalmazási nehézség tévhitét is. Ha a teljes RUP leírást látjuk, a következő kérdések merülhetnek fel a konkrét probléma megoldása kapcsán: • mely lépéseket vegyem figyelembe? • a lépések milyen sorrendben kövessék egymást? • mik a legszükségesebb lépései a folyamatnak? A RUP szerencsére „testreszabható”, jól adaptálható és hasznos lehet egy szoftverfejlesztő folyamatát javítani akaró cég számára, amennyiben megértik az alapokat. A RUP alapját a következő (filozófiai) elvek adják: • a szoftver projekt csapatnak előre kell tervezni • mindenkinek és minden pillanatban tudnia kell merre tart a projekt 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 13 • a projektben
felhalmozódó tudást egy tárolható és bővíthető formában kell rögzíteni • a „legjobb módszerek” elve A „legjobb módszerek” elve gyűjtőnév arra nézve, hogy mi a folyamat legracionálisabb felépítése. Ez nem más, mint a nagy tapasztalati tudással rendelkező szoftverfejlesztők „folklórja”, tehát az a módszer, amit nagy gyakorlat után az embernek a „józan paraszt ész” diktál. Ez egészben az a nagyszerű, hogy erre nem saját magunknak kell rájönni, mivel már mások leírták. Ez az elv szerint a következő tulajdonságai vannak a folyamatnak: 2.32 • Használati eset (Use case) vezérelt. A legfontosabb szempont a teljes folyamat során az, hogy maga a rendszer és a rendszer felhasználóinak kapcsolatai irányítják a lépéseket. • Architektúra központú. Egy jól meghatározott architektúrára épül, az architektúra komponensei közötti kapcsolatok tisztázottak. • Iteratív. A probléma és a megoldás kis
részekre van bontva, minden iterációs ciklus egy ilyen kis részt céloz meg. • Inkrementális. Mindegyik iterációs ciklus növekményesen épít az előző iteráció eredményére, így a rendszer egyre „jobb” lesz. • Vezérelt. A legfontosabb, hogy az egész tevékenységet egy folyamat vezérli, ahol mindig lehet tudni mi a következő lépés. A menedzsment is lehet vezérelt, ha az összes eredmény, átadandó (deliverable), dokumentum és programkód konfigurációs menedzsment alatt van. Az iterációk (Az alábbi leírásban hivatkozunk a UML nyelv egyes elemeire, amelyek a 2.4 részben vannak ismertetve.) Mint ahogy azt korábban is láttuk, az időciklus négy fő fázisra van osztva. Az első, kezdeti fázis a felkészülést jelenti (Inception Phase), valódi fejlesztés itt még nem történik. Itt legtöbb esetben egy iteráció van, a 0 iteráció Ebben az iterációban lehetőség nyílik arra, hogy széltében és mélységében is megvizsgáljuk a
rendszerrel szemben támasztott követelményeket és célokat. A legfontosabb célja ennek az iterációnak, hogy a megvalósíthatóságra becslést kapjunk, hogy egyáltalán bele vágjunk-e a fejlesztésbe. Általában a fejlesztést addig nem is kezdjük el, amíg a megtervezett architektúrát felépít ő ötleteinkről bebizonyosodik, hogy helyesek és a rendszer üzleti modelljét teljes egészében megértettük. A 0. iteráció lépései a következők: 1. Azonosítsuk a rendszer legfontosabb és szembeötlő funkcionális és nemfunkcionális követelményeit. Itt a felhasználó számára látható funkcionális követelményekhez készítjük el a használati eseteket. A használati esetek önmagukban azonban nem elegendőek, a nem-funkcionális követelményeket normál, szöveges dokumentumban rögzítjük. 2. Azonosítsuk a modellezni kívánt területhez (domain-hez) tartozó osztályokat. Az osztályok meghatározása a követelményekből többféleképpen is
történhet: használhatunk CRC (Class/Responsibility/Collaborator) kártyákat, Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 14 2.3 A folyamat adatbányászat segítségével is dolgozhatunk, előzetes területi (domain) tapasztalat alapján, vagy egyszerűen megkereshetjük a követelményeket tartalmazó dokumentumban a főneveket és a főneveket tartalmazó kifejezéseket. Próbáljunk ki több módszert is, az osztályok megtalálásához egy módszer ritkán elég. 3. Határozzuk meg az osztályok feladatát és az osztályok egymással való kapcsolatait. A feladatok az osztályok stratégiai céljai Egy osztály feladata határozza meg, hogy milyen műveletei lesznek az osztálynak. A műveletek pedig meghatározzák az adattagokat. Soha ne az adattagok meghatározásával kezdjük egy osztály tervezését. Ez utóbbi adat-modellezés, nem pedig objektumorientált objektum-modellezés. 4. Készítsük el a terület
osztálydiagramját (domain class diagram). A területosztálydiagram egy oldalnyi anyag osztályokkal és azok neveivel Nem tartalmaz kapcsolatokat, műveleteket, sem adattagokat egyelőre. A feladatok definíciójával együtt ez a diagram egy közös szótárt nyújt a projekt számára. 5. Az összes eddig azonosított használati esetet (use case-t) és osztálydefinicíót tároljuk el egy OO CASE eszköz segítségével. Egy CASE eszköz használata minden nem-triviális projektben elengedhetetlen. Megkönnyíti a modellezést és az elkészített dokumentációk is áttekinthetők maradnak. Nagyon fontos, hogy a nagy rizikójú problémákat az első iterációkban dolgozzuk fel. A CASE eszköz ezek felismerésében is segít. 6. Keressük meg a legnagyobb rizikótényezőket, állítsuk fontossági sorrendbe az architektúra szempontjából jelentős használati eseteket és diagramokat. Ne foglalkozzunk az összes használati esettel és minden egyes részlettel. Azt a
20%-ot válasszuk ki, ami a 80%-t adja a legfontosabb rendszerfunkcióknak. A későbbi iterációkba vegyük bele a kevésbé fontos használati eseteket. 7. Osszuk fel a nagyobb használati eseteket és diagramokat az egyes iterációs ciklusok között (iterációk megtervezése). Minden iterációnak határozzuk meg a célját, a menetrendjét, a rizikóit és a kimenetét (eredményét). 8. Készítsünk egy iterációs tervet, amely meghatározza azt a „mini-projektet”, amit az egyes ciklusokban elkészítünk majd. Legyenek az iterációs ciklusok fókuszáltak és korlátosak (3-4 hét egy iterációra jó becslés). Törekedjünk arra, hogy minden iteráció egy mini-vízesés projekt legyen, részenként teljesítve a rendszer követelményeit. Minden egyes iteráció leírásának tartalmaznia kell a követelményeket, az analízist, tervezést, implementációt és a tesztelést. A 0. iterációban a projekt teljes méretet és legfontosabb jellemzőit vizsgáltuk
meg, és végeredményként egy „csináljuk – ne csináljuk” döntést hozunk a projekttel kapcsolatban. Mint ahogyan azt már korábban említettük, a folyamat szerint a részleteket iteratívan alakítjuk ki, ami azt jelenti, hogy a további iterációk lépéseit ismételve hajtjuk végre minden egyes iterációban, ahogyan azt a kezdeti iteráció(k)ban megterveztük. Ezen iterációk mindegyikében szükség szerint analízis (mit csinál a rendszer) vagy tervezési (hogyan csinálja a rendszer) jellegű feladatokat végzünk el. A további iterációk lépései a következők: 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 15 1. Teremtsünk kapcsolatot a használati esetekben található működési leírás és a terület-osztálydiagram között. Ezzel analízis-szintű kölcsönhatás diagramokat (szekvencia és együttműködési) hozunk létre az iterációban kijelölt
minden egyes használati esetre. Ezen dinamikus diagramok használatának legnagyobb előnye, hogy segítenek megtalálni az osztályok műveleteit. 2. Próbáljuk ki és ellenőrizzük az analízis-szintű szekvencia diagramokat papíron, táblánál vagy a gépünk képernyőjén. Ez konkrétan azt jelenti, hogy kövessük az üzenetek folyását a kölcsönhatás diagramokon. Bővítsük az osztálydiagramokat a megtalált műveletekkel és adattagokkal. Ne foglalkozzunk teljesítmény vagy technológiai vonatkozásokkal az analízis fázisában. Az analízis során a problémákat derítjük fel, a tervezés során határozzuk meg a megoldást. Próbáljuk ezeket a fázisokat gondolatban is elkülöníteni. 3. Készítsünk analízis-szintű állapotátmeneti (statechart) diagramokat minden olyan osztályhoz, amely „kitüntetett” állapottal rendelkezik. Az állapotátmeneti diagramok egy osztály „állapotterét” határozzák meg, azaz egy objektummal foglalkoznak és
ez már sokkal részletesebb leírás, mint a szekvencia diagramok esetében, ahol több objektum egymás közötti üzenetváltásait ábrázoljuk egy használati esetet megvalósítandó. 4. Bővítsük a szekvencia és állapotátmeneti diagramokat tervezési szintű tartalommal. A tervezés-szintű gondolkodásban az osztálydiagramokat és a szekvencia diagramokat bővítjük kiegészítő- és tervezés-szintű osztályokkal (tároló osztályok, GUI és más technikai osztályok, adattípusok, stb.) A tervezés során minden iterációban a teljesítményt és technikai meggondolásokat is figyelembe kell venni. 5. Próbáljuk ki a tervezési-szintű szekvencia és állapotátmeneti diagramokat. Ha új műveleteket vagy új adattagokat fedezünk fel, adjuk azokat hozzá a megfelelő osztályokhoz. A tervezés eredménye már tartalmaz konkrét operáció (metódus) neveket, paramétereket és visszatérési típust. 6. Frissítsük az OO CASE programunkban tárolt
információkat és az új változatot a projekt többi tagjának is jutassuk el. Az összes diagramot és egyéb dokumentumot frissítsük az iteráció végén. A frissítést a projekt többi tagjához is jutassuk el. (A dokumentáció nem lehet 24 óránál régebbi!) 7. Implementáljuk a jelenlegi iterációban kijelölt használati eseteket az aktuális diagramok segítségével (programkódot elkészítése). Ne feledjük el, hogy a tervezés nem programkód készítés. A tervezés a szükséges előkészület a kódoláshoz. 8. Teszteljük a kódot az aktuális iterációban. A tesztelés folyamatos: teszteljünk a legfelső (projekt) szinten és a legkisebb (unit) szinten is. 9. Értékeljük ki az iteráció eredményeit. Az eredmény kiértékelése a legfontosabbakra koncentrálódjon: nézzük meg mi sikerült és mi nem az iteráció során. Sikerült-e elérni kitűzött célokat az iterációra vonatkozólag? Tovább tartott-e az iteráció a tervezettnél?
Sikerült-e hozzáadni minden tervezett funkciót? Ha valami nem sikerült, határozzuk meg a probléma okát és javítsuk ki (szükség szerint visszatérve az aktuális iteráció korábbi lépései valamelyikére). Döntsük el, hogy tudjuk-e követni a meghatározott tervet Ha nem, akkor megfelelően módosítsuk az iterációs tervet. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 16 2.4 Unified Modeling Language 10. Lássunk neki a következő iterációnak az 1 lépéstől kezdve további használati esetek hozzávételével. Az iterációk 1–9 lépéseit addig hajtsuk végre, amíg rendszerünk teljesen el nem készül. Ez a lényege az iterációs és inkrementális fejlesztésnek: hajtsunk végre egy új iterációt, növekményesen építve az eddigi eredményekre. A fent ismertetett folyamat nem tartalmazza az összes lépést, amely egy teljes, minimális RUP végrehajtásához szükséges. Egy járható utat mégis
biztosít a UML és RUP fontosabb elemeinek megismerésére, megkönnyítve a további elemek elsajátítását, és ezáltal a folyamat későbbi igények szerinti testreszabását. Nem árt azonban szem előtt tartani, hogy a folyamat önmagában sem annak mechanikus alkalmazása a sikert nem garantálja. A fent leírt egyszerűsített változat azonban hasznos útmutató lehet a teljes RUP-ra való átálláshoz. Összegzésként jegyezzük meg, hogy a folyamat alapfilozófiája nagyon egyszerű: tudd, hogy merre tartasz, és a feladatot részenként oldd meg! A legfontosabb szabály: a folyamat szolgálja a fejlesztést, és ne fordítva! 2.4 Unified Modeling Language 2.41 Bevezetés helyett: az architektúra Ahogy az a korábbiakból ismeretes, a szoftverfejlesztés során a folyamat segítségével folyamatosan analizáljuk, tervezzük és építjük meg a rendszerünket. A folyamat közben azonban a rendszert minden pillanatban egy bizonyos szemszögből vizsgáljuk, attól
függően, hogy éppen mi az aktuális vizsgálati szempont vagy fejlesztési cél. A modellezéskor keletkező modell, mint a rendszer teljes leírása ilyen nézetek (view) által jelenik meg a tervezési dokumentációban. Minden nézetre jellemző, hogy a rendszer architektúráját írja le a reá jellemző módon. Más szóval, amikor a nézetekről beszélünk, azokat együttesen rendszerarchitektúrának is nevezhetjük. Ezen belül, az egyes nézetekben a jelölésrendszer által nyújtott diagramokat használjuk. Az alábbi ábra mutatja, ahogyan a folyamat vezérli a tervezést és ezáltal a rendszer architektúrája épül. Az architektúra különböző nézeteinek megvalósulásai maguk a diagramok, amiket a jelölésrendszer definiál. Folyamat (RUP) Architektúra (4+1) Jelölés (UML) Modellezéskor a rendszer architektúráját az alábbi alapvető nézet típusokkal írjuk le (az architektúra „4+1” nézete): 2002, Beszédes Árpád és Ferenc Rudolf
Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 17 K om p on e n s n éze t (C om p on e n t Vie w ) Log ika i n éz et (L og ic al Vie w ) - I m ple m e nta tio n V é g felh asz n á ló F u n kcio na litá s H as zn á lati E set n éze t (U se C ase View ) F oly am a t n éz et (P ro ces s V ie w ) P ro g ra m oz ó k S zoftve r m e n ed zsm e n t Ú jra felh a szná lható sá g P o rta b ilitás Te lep ítési n éz et (D ep loym en t View ) R en d sz er in teg rá to ro k T e lje sítm é ny S ká lá zh a tó sá g H ib a tű ré s R e n d sz e r te rv ez é s R e n d szerto p o ló g ia Á ta dá s, te le p ítés K a p cso la tta rtás Elvi (log ik ai) F iz ikai • Logikai nézet. A rendszer logikai szerkezeti felépítésének ábrázolására használatos. Szerkezeti és viselkedési modellezéskor tipikusan használt diagramfajta az osztálydiagram és a kölcsönhatás diagramok. • Komponens (implementációs)
nézet. Implementáció-közeli ábrázol, a használt diagramfajta a komponens diagram. • Folyamat nézet. A folyamat egyéb jellemzőinek fenntartott nézet, különböző nem-funkcionális szempontok szerint ábrázolja az architektúrát: pl. futás közbeni kérdések. • Telepítési (feladatkiosztási) nézet. Magasabb szintű fizikai nézet, a rendszer magas szintű szerkezetére vonatkozóan, telepítési diagram használatos. • Használati eset nézet (esettanulmány). Az egészet összefogja és közös kommunikációs platformot létesít a résztvevő felek között. Használati eset- és kölcsönhatás diagramok alkalmasak. szerkezetet Természetesen nem minden tervezendő rendszer igényli az összes nézet használatát. A folyamat határozza meg, hogy mely nézeteket és diagramok kell alkalmazni az egyes lépésekben. Másfelől, ha szükséges, új nézetek is definiálhatók, ha a rendszer speciális tulajdonságokkal rendelkezik (pl. adat,
biztonság). További fogalmak. Modellnek nevezzük a rendszer teljes leírását egy bizonyos szemszögből (a szoftverfejlesztéshez szükséges szempontok szerint), tehát a modell tartalmazza az elkészítendő szoftverrendszer szabványos ábrázolását. A modell egy-egy nézete adja az analízis és tervezés során szükséges vetületeket, például a fenti architektúra „4+1” nézetet. Maguk a diagramok (mint a jelölésrendszer alapelemei) ezen nézetek megvalósulásai. A diagramra jellemző, hogy: • Mindig valamely résztvevő szemszögéből ábrázolja a rendszert, pl. tervező, kódoló, megrendelő, • Csak részleges reprezentációja a rendszernek. • A modell egy-egy eleme több nézetben (diagramban) is megjelenhet egyidejűleg. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 18 2.4 Unified Modeling Language • Szemantikailag konzisztens a többi nézettel. • Két alapvető nézet típust
különböztetünk meg, amelyek általában minden diagramra vonatkoztathatók: • osztályozási nézet, pl. Element <<covalent>> C Carbon Hydrogen H C <<covalent>> • C példányosítási nézet, pl. :Hydrogen 2.42 :Hydrogen :Hydrogen :Carbon :Carbon :Hydrogen :Hydrogen :Hydrogen UML áttekintése A UML (Unified Modeling Language) magyarul annyit jelent, mint egységesített modellező nyelv. Azért nyelv, mert egy nyelvezetet biztosít vizuális dokumentáció készítéséhez. Az általa definiált elemek, kapcsolatok, diagramok, stb bizonyos szintaktikai és szemantikai szabályok összességének felelnek meg, mint minden más nyelv esetében is. Egy vizuális nyelv esetében a szintaktika a grafikus kinézetet jelenti, a szemantika meg a megjelenített elemeknek értelmezést ad és további szemantikai szabályokat definiál. A UML egységesített, mert számos korábban meglévő nyelv ötvözésével keletkezett, azokból kiragadva
a legjobb tulajdonságokat. A UML egyik legfontosabb jó tulajdonsága éppen ez, ugyanis hatalmas tapasztalati tudásra épít. Ezáltal nyugodtak lehetünk afelől, hogy a nyelv alkalmazásával csakis nyerhetünk. Tehát a UML nem egy cég által kifejlesztett és jól marketingelt termék, hanem egy megbízható, és jól bevált módszer. A UML olyan rendszerek kezelésére alkalmas, amelyek nagy mértékben szoftver alapúak (elvileg nem szoftver-jellegű elemek is használhatók). Konkrétan, ezen rendszerek elemeinek: • vizualizálására, 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design • specifikálására, • létrehozására, és • dokumentálására használatos. 19 A UML nyílt szabványként tekinthető (Object Management Group – OMG által), hiszen a hivatalos specifikációjáért egy komoly testület felelős, amely mögött számos neves intézmény áll, mint
pl. Rational, HP, IBM, Microsoft, Oracle, Platinum, TI, Sun, DEC, Compaq, stb. Ezen kívül, a nyelvet támogató szoftvereszközök skálája is egyre bővül (pl Rational Rose, Microsoft Visual Studio, Microsoft Visio, GDPro, Telelogic Tau, dia). A UML-nek számos nyilvánvaló előnye van, ezek közül néhány: • Könnyű elsajátítani, de ugyanakkor tetszőleges bonyolultságú modelleket is lehet vele készíteni, különböző kifinomult technikák segítségével. • Mindössze 10-20 % része van használva az esetek 80-90 %-ban, ami azt jelenti, hogy általában nem szükséges a teljes specifikációt ismerni, csak annak egy jól körülhatárolható részét. • Implementációtól független tervezést tesz lehetővé. • Teljes szoftverfejlesztési életciklust támogatja (ld. RUP) • Különböző alkalmazás területeken valósidejű, beágyazott, stb.) használható alkalmazható: üzleti, UML története. Számos módszertan és jelölés járult
hozzá a UML létrejöttéhez, de alapvetően az OMT, Booch és OOSE módszerek egyesítésével jött létre 1995 októberében. Ezekből elsődlegesen a szemantikus modellek (mi az amit ábrázolni kell), a szintaktikus jelölések (grafikus megjelenés) és nyilván a diagramok lettek „unifikálva”. Az első szabványosított specifikációt (10) 1997 júliusában adta ki az OMG (Object Management Group). A jegyzet írásakor aktuális verzió az OMG UML 1.4 (2001 szeptember) Maga a UML specifikáció egy vaskos dokumentum (az 1.4-es verzió 566 oldalas) és a következő főbb részei vannak: • Jelölés (Notation). A szintaxist definiálja, azaz a jelölésrendszer elemeit • Szemantika (Semantics). A szintaxisnak értelmet ad, leírja az egyes jelölések jelentését. • Object Constraint Language (OCL). További megszorítások megadására szolgáló nyelv definiálása, amellyel a UML hatékonyan bővíthető. • Modellek cseréjére (szoftver eszközök
megoldások: XMI és CORBA IDL. • Testreszabott UML. 2 speciális profile van definiálva (üzleti és valósidejű) a szabványos bővíthetőségi mechanizmusok segítségével. • Standard elemek. Néhány elem előre van definiálva, mint „foglalt kulcsszavak”. együttműködésére) vonatkozó UML metamodell architektúra. A specifikáció egy háromszint ű metamodell szerkezet segítségével definiálja a nyelvet. E rendszer lehetővé teszi a lehető Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 20 2.4 Unified Modeling Language legáltalánosabb leírást és a bővíthetőséget is precízen definiálja. Azért előnyös a használata, mert mindegyik szinten maga a UML van használva a leírásra. A legfelső szint a meta-metamodell, amely azt írja le, hogyan kell a metamodell szinten definiálni a UML nyelvet. A második szint a metamodell, amely magát a UML nyelv szintaxisát írja le. Ez alatt
helyezkedik el a felhasználó analízis szint ű modellje a konkrét modellezési elemekkel. Ez alatt még elhelyezkedhet az objektumok rétege, ahol az analízis szintű modell osztályainak példányai szerepelnek. A specifikáció szempontjából a legfontosabb a metamodell, amely megfelelően csoportosítva részletesen leírja, hogy hogyan kell hogy kinézzenek az egyes UML diagramok. Meta-Metamodel Layer (M3): meta-metaosztályok a UML metamodell hez <<metamodel>> MOF Metametamodel Metamodel Layer (M2): metaosztályok a UML modellhez pl. Class <<metamodel>> UML Metamodel Model Layer (M1): osztályok a felhasználó modelljéhez pl. Passenger, Ticket, TravelAgency User Model :Foo :Bar User Obj ects Layer (M0): Felhasználói objektumok, a UML model oszályainak példányai :Baz A metamodell az alábbi építőkövek segítségével írja le a nyelvet. Minden használatos diagramfajtánál ezeknek valamilyen kombinációját alkalmazzák: • •
Modellezési elemek. A modellezési elemek azok az entitások, amelyek valamilyen részét modellezik a rendszernek. A következő alapvető modellezési elemek léteznek: szerkezeti elemek: class, interface, collaboration, use case, component, node viselkedési elemek: interaction, state machine csoportosítási elemek: package, subsystem egyéb: note Kapcsolatok (relationships). A modellezési elemeket, mint csomópontokat összekötő élek. A kapcsolatok fajtái: függőségi kapcsolat (dependency) asszociációs kapcsolat (association) általánosítás, öröklődés (generalization) megvalósítás (realization) 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design • • 21 Bővíthetőség (Extensibility Mechanisms). A UML nyelv bővítésére nyújtott támogatások: sztereótípus (stereotype) címke-érték (tagged value) megszorítás
(constraint) Diagramok. 9 diagramfajta van definiálva: Használati eset (Use Case D.) Funkcionalitás felhasználó szemszögéből. Osztálydiagram (Class D.) A rendszer „szótára”: osztályok és kapcsolataik. Objektum diagram (Object D.) Osztály példányok és kapcsolataik Komponensdiagram (Component D.) Implementáció fizikai szerkezete Telepítési diagram (Deployment D.) A rendszer hardver topológiája Szekvenciadiagram (Sequence D.) Dinamikus viselkedés (időorientált) Együttműködési diagram (Collaboration D.) Dinamikus viselkedés (üzenet-orientált). Állapotátmeneti diagram (Statechart). Dinamikus viselkedés egy objektumra (esemény-orientált). Aktivitás diagram (Activity D.) Dinamikus viselkedés egy objektumra (aktivitás-orientált). UM L d iag ra m S tatiku s d iag ram Din a m iku s d iag ram H aszn álati E set (Use Ca se) O sztály D . (C la ss) O b je ktu m D . (O bject) E g yüttm ű köd é si (Collab
oration ) S ze kve n cia (S eq u en ce) K o m p o n en s D . (Com p on en t) Telep íté si (D ep loym en t) Állap otátm en eti (S tate ch art) Aktivitás D . (Activity) A fenti építőkövek segítségével nagyon sok szempontból leírható a modellezni kívánt rendszer. Általában három csoportra osztják a modellezés mibenlétét (és ezáltal a használt elemeket és diagramokat): • Szerkezeti modellezés. Ez a fajta modellezést a rendszer szerkezetének leírásánál használják. Jellemző diagramok az osztály-, objektum-, komponens- és telepítési diagram. • Használati eset modellezés. A rendszer viselkedésének leírására alkalmas ahogyan az kívülről látszik. Használati diagram használatos Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 22 2.4 • Unified Modeling Language Viselkedési modellezés. A rendszer (futás közbeni) viselkedésének leírásakor modellezünk ily módon.
Konzisztencia. A UML specifikáció különböző módon segíti azt, hogy a modell és a diagramok a modellezés minden pillanatában konzisztens maradjon. A legalapvetőbb szempont a UML által definiált szintaktikus és szemantikai szabályok betartása, azaz hogy a modell megfeleljen a nyelvnek. Ezen kívül, egyéb apróságoknak is fontos figyelmet szentelni, mint pl. a nevek stílusa, láthatósági szabályok betartása, a diagramok integritása, stb. Nagy segítség még az OCL OCL (Object Constraint Language), mint bővíthetőségi technika, mely segítségével pontosabbá tehető a modell. A specifikáció különböző szinteken segít: 2.43 • Először is, definiálja a jelölést (notation). Ez kötelezően betartandó Ide tartozik pl. az, hogy egy asszociáció végpontot mivel lehet kiegészíteni • Megadja a lehetséges jelölési módozatokat, prezentáció (presentation options). Azt határozza meg, hogy a használható jelölés-kombinációknak melyek
az értelmes módozatai. • Stílus (style guidelines). További stilisztikai megoldásokat javasol, amelyek egységes megjelenésű ábrákat eredményez, pl. osztálynevek nagybetűvel kezdődjenek. Szerkezeti modellezés A rendszer olyan nézetét, amely az objektumok és egyéb dolgok szerkezetét írja le, szerkezeti modellnek nevezzük. Szerkezeti modellezéskor a rendszer vázát írjuk le, amely valójában a felépítést ábrázolja különböző szempontok szerint: • logikai és fizikai szerkezet, • különböző osztályozások (pl. osztálydiagram), • objektumok szerkezete, • kapcsolatok, Egyik alapvető ismérve a szerkezeti modelleknek, hogy azok nem ábrázolnak semmiféle időtől való összefüggéseket, mint pl. állapotok, üzenetek, A szerkezeti modellek alapvető elemei a következők: lem Leírás sztály (class) azonos tulajdonságú objektumok halmazának leírása nterface operációk nevesített halmaza, amely egy viselkedést ír
le omponens component) moduláris és cserélhető egysége a rendszernek, amely interfészeket implementál somópont node) futás közbeni fizikai feldolgozó egység 2002, Beszédes Árpád és Ferenc Rudolf Szintaxis «interface» Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design egszorítás constraint) szemantikus megszorítás 23 feltétel vagy {constraint} A fenti elemeket a szerkezeti modellek kapcsolataival lehet összekapcsolni: Elem Leírás Szintaxis asszociáció (association) kettő vagy több osztályozási elem közötti szemantikus kapcsolat, amely a létrejövő objektumok között fog összeköttetést eredményezni aggregáció (aggregation) speciális asszociáció, amely rész-egész kapcsolatot ír le általánosítás osztályozó kapcsolat egy általánosabb (generalization) és egy speciálisabb elem között függőség (dependency) két elem közötti kapcsolat, amelyeknél az egyik
változása befolyásolja a másikat megvalósítás (realization) egy specifikáció és annak megvalósítása közötti kapcsolat (pl. osztály és interface) A fenti elemek és kapcsolatok felhasználásával a következő szerkezeti diagramokat definiálja a UML: • Statikus szerkezeti diagramok (logikai, elvi): osztálydiagram (class diagram) és objektum diagram (object diagram). • Implementációs diagramok (fizikai): komponens diagram (component diagram) és telepítési diagram (deployment diagram). Példák osztályokra: Window Window size: Area visibility: Boolean display () hide () Window {abstract, author=Joe, status=tested} +size: Area = (100,100) #visibility: Boolean = true +default-size: Rectangle #maximum-size: Rectangle -xptr: XWindow* +display () +hide () +create () -attachXWindow(xwin:Xwindow*) Példák asszociációs kapcsolatra: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 24 2.4 Unified Modeling
Language Job 1.∗ ∗ employer employee Company Job salary Person boss 0.1 worker ∗ Manages Person {X or} Account Corporation Példák kapcsolat végpontjaira: 1 Polygon +vertex 3.∗ Contains {ordered} Point 1 GraphicsBundle 1 -bundle color texture density Példa kompozíciós kapcsolatra: Window scrollbar [2]: Slider title: Header body: Panel Window 1 scrollbar Slider 2 1 1 title 1 Header body 1 Panel Példa az általánosítás kapcsolat (öröklődés) kétféle ábrázolására: 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 25 Shape Separate Target Style Polygon Spline Ellipse . Shape Polygon Shared Target Style . Spline Ellipse Példák függőségekre: ClassA ClassD ClassB «friend» «friend» operationZ() «instantiate» «call» ClassC «refine» ClassC combines two logical classes ClassE ClassD Controller «access» «access»
«access» Diagram Elements «access» «access» Domain Elements Graphics Core Az objektum diagram olyan osztály diagram, amelyben csak objektumok szerepelnek és a közöttük levő összeköttetések (link). Példa objektumokra és objektum diagramra: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 26 2.4 Unified Modeling Language tria n g le : P olyg on tria ng le c e n te r = (0 ,0 ) ve rtic e s = ( (0 ,0 ),(4 ,0) ,( 4,3 )) bo rd e rC olo r = bla c k fillC o lo r = wh ite : P olyg on tria ng le : P o lyg o n s c h e d u le r awindow : Window horizontalBar:ScrollBar verticalBar:ScrollBar moves surface:Pane moves title:TitleBar officer Jill:Person member treasurer member downhillSkiClub:Club president Joe:Person member Chris:Person officer Szerkezeti diagramok egyéb elemei közé tartoznak a megszorítások és megjegyzések, pl.: ∗ Me m b e r-of ∗ P e rs o n Co mm itte e {s ub s e t} 1 C h a ir-o f
∗ e m p lo ye e ∗ 0 . 1 P e rs o n ∗ R e p re s e n ts a n in c o rpo ra te d e n tity. e m p loye r 0 . 1 C o m pa n y b os s {P e rs o n. e m p loye r = P e rs o n. bo s s e m p lo ye r} 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 27 Példa komponens diagramra: <<auxiliary>> CatalogInfo <<focus>> Catalog <<auxiliary>> CatalogPK <<reside>> <<reside>> <<reside>> <<ejbEntity>> Catalog <<implement>> <<file>> CatalogJAR Példa telepítési diagramra: primaryServer:AppServer <<database>> :AccountsDB :QuoteService primaryBroker :BondBroker <<become>> backupServer:AppServer backupBroker :BondBroker :QuoteService 2.44 <<database>> :AccountsDB Használati eset modellezés Használati eset modellezéskor a rendszer
viselkedését írjuk le, ahogyan az egy külső szemlélő szemszögéből látszik. Ez azt jelenti, hogy a rendszer belső szerkezetéről vagy viselkedéséről nem tesz említést. Leírja a rendszer funkcionalitását, a főbb tevékenységeit és kapcsolatát a külvilággal. Használati eset modellezés a fejlesztési ciklus kezdeti fázisaira jellemző, amikor a rendszer funkcionalitását definiáljuk vagy fedjük fel. Használati eset modellezésnek fő kellékei a használati esetek és a szereplők, amelyekből maguk a használati eset diagramok épülnek fel. A használati eset diagram jó kommunikációs eszköz a fejlesztőcsapat és egyéb résztvevők között. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 28 2.4 Unified Modeling Language A használati eset modellezés alapvető elemei a következők: Elem Leírás Szintaxis használati eset tevékenységek sorozata, amelyet a (use case) rendszer végre tud
hajtani a szereplőkkel kommunikálva szereplő (actor) UseCaseName szerepek összefüggő halmaza, amelyeket a használati eseteket használók játszhatnak ActorName rendszerhatár fizikai rendszer és a szereplők közötti határvonal A fenti elemeket összekötő kapcsolatok a következők: Elem Leírás Szintaxis asszociáció (association) jelzi, hogy a szereplő részt vesz a használati esetben általánosítás (generalization) osztályozó kapcsolat egy általánosabb és egy speciálisabb elem között (itt: használati eset) kibővítés (extend) kibővített használati esettől alap használati eset felé (bővített tevékenység) magábanfoglalás (include) alap használati esettől magában foglalt használati eset felé (résztevékenység) <<extend>> <<include>> Példák használati eset diagramokra: Telep hon e Catalog Che c k s tatus Place orde r S ales pe rs o n Fill orders S hipping Cle rk Cus tomer Es ta bl is h c
re di t S upe rvis o r 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 29 Order Product Supply Customer Data «include» Arrange Payment «include» «include» Place Order 1 * «extend» Extension points additional requests : the salesperson asks for the catalog after creation of the order Request Catalog 2.45 Viselkedési modellezés A harmadik típusú modellezés a viselkedési modellezés, amikor a rendszer működés közbeni viselkedését írjuk le. A viselkedés leírásakor a belső működés részleteit adjuk meg, ahogy az az időtől való függés szerint történik. A viselkedést különböző típusú kölcsönhatások (interakciók) segítségével adjuk meg. Ezek lehetnek: • objektumok között (ezek a kölcsönhatás diagramok: szekvencia- és együttműködési-), • objektumon belül (állapotátmeneti- és aktivitás-) és • egyéb fajta
példány elemek között (pl. adatérték, komponens-példány) A szekvencia diagram főbb elemei példán bemutatva: object symbol name : Class other lifeline stimulus name () activation new () : Class delete return create További példa vezérlési szerkezetekkel és feltételekkel: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 30 2.4 calculator Unified Modeling Language filter value [ x > 0]: getValue () [ x < 0]: transform () getValue () iterate () Példa együttműködési (collaboration) diagramra: object symbol link symbol standard stereotype redisplay () stimulus window : Controller standard stereotype 1: displayPositions (window) 1.1 *[i := 1.n]: drawSegment (i) window «parameter» standard constraint 1.131 add (self) wire contents {new} «local» line wire :Wire standard stereotype : Window 1.12: create (r0, r1) 1.13: display (window) «self» 1.11a: r0 := position () 1.11b: r1
:= position () left : Bead {new} : Line standard constraint right : Bead Állapotátmeneti diagram főbb elemei: fels felső szint szintű Kezd Kezdőő ppszeudo-állapot s zeudo eudo--állapot Állapot Állapot top Indítóesemény Indítóesemény Ready Átmenet stop /ctr := 0 Done Végállapot Végállapot Akció Akció stop 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 31 Példa állapotátmeneti diagramra: Példák aktivitás diagramra: Calculate Cost [cost < $50] Charge Account [cost >= $50] Get Authorization Customer Telesales Accounting Warehouse Request Return Get Return Number Ship Item Receive Item Item [returned] Restock Item Credit Account 2.46 Item [available] UML modell szervezése Vizuális modellezéskor szinte kizárólag nagy méretű és bonyolult szoftver rendszerek megvalósítása a cél (végső soron ezért használunk vizuális
modellezési technikákat, ld. bevezetésben) Ekkor viszont elkerülhetetlen az, hogy a nagy Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 32 2.4 Unified Modeling Language modellünket valamilyen módon emészthető formában tálaljuk, azaz megfelelő módon osszuk kisebb egységekre. A triviális objektumorientált csoportosítási elemek az osztályok, illetve magasabb szinten a komponensek. Azonban a UML további hatékony eszközöket is nyújt a csoportosításra, ezek a következők: • csomagok (package), • alrendszerek (subsystem) és • modellek (model) Csomagok. A csomag modellezési elemek csoportosítására szolgál és általánosan használható, de legnagyobb jelentősége a szerkezeti modellezésnél van. Különböző elemeket is tartalmazhat, további csomagokat is, így egy hierarchikus szerkezetet képezve. Egy csomag elemeinek láthatósági attribútumokat lehet megadni, amelyek más csomag elemei
számára korlátozzák az elérhetőséget. Csomagok között függőségi kapcsolatok lehetnek. Példa csomag jelölésre: Pl. UML metamodell legfelsőbb szintű csomagja: «metamodel» UML Model Management Behavioral Elements Foundation Alrendszerek. Míg a csomagokba történő szervezés főleg a logikai szerkezet felé irányul, az alrendszer olyan fajta csoportosítás, amelyet a fizikai felépítés ábrázolásánál alkalmazunk. Tehát az alrendszerek összessége a végső elkészült 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 33 rendszerünket alkotja. Az alrendszer megmutatja a kapcsolatot a specifikáció és a megvalósítás között, pl. használati esetek és osztályok között Általában osztott rendszerek modellezésekor szokták alkalmazni. Az alrendszer általános jelölése: Traffic Control Realization elements Operations changeDigitAnalysisInformation ( )
«realize» Specification elements Initiate Call changeDigitAnalysisInformation ( ) : : Receive Digit and Connect Hook Signal and Disconnect Az alrendszer jelölés használatára számos lehetőség van, pl. állapotátmeneti megközelítés: Traffic Control Specification elements Stopped Running Maintenance Error Exhausted Pl. alrendszerek és interfészek kapcsolataira: Trunk Traffic Control Subscription Trunk Traffic Control Subscription Modellek. A korábban megismert modell fogalmát UML diagram szinten is meg lehet jeleníteni. A modell tehát a rendszernek egy olyan leképezése, amely egy bizonyos céllal készül és ez a leképezés a rendszer teljes reprezentációját adja, a teljes modellezési adathalmazt ábrázolja. A modelleket diagramok formájában Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 34 2.4 Unified Modeling Language nagyon ritkán ábrázolják, az inkább a modellező eszköz belső
tárolási formájaként jelenik meg és valamilyen strukturális formában szerkeszthet ő. A UML metamodell leírására viszont például ez a jelölés alkalmas. Az alábbi példa mutatja az analízis és tervezés szint ű ábrázolását ugyanazon rendszernek (a függőségi kapcsolat ugyanazon elem két nézetét köti össze): Analysis Design «trace» 2.47 Bővíthetőségi mechanizmusok Mint ismeretes, a UML nyelv egyik legfontosabb tulajdonsága, hogy általános, azaz tetszőleges típusú szoftver rendszer modellezésére alkalmas. Mégis, lehetnek esetek, amikor a modellezni kívánt rendszer tartománya (domain-je) további jelöléseket kíván, vagy olyan tulajdonságok ábrázolására van szükség, amelyeket a UML nyelv nem tesz lehetővé. Erre adnak megoldást a UML bővíthetőségi mechanizmusai, a sztereótípusok, az OCL és a címke-érték (tagged value). Sztereótípusok. A stereotype olyan fajta bővítését teszi lehetővé a UML-nek, amely
segítségével további szemantikát lehet definiálni az egyes UML elemekhez. Szinte minden elemhez definiálható sztereótípus (osztályokhoz, kapcsolatokhoz, csomagokhoz, ). Vannak azonban előredefiniált sztereótípusok is Egy sztereótípus gyakorlatilag a UML metamodellt egészíti ki újabb metaosztályokkal, amelyeket a modell használhat. Alapvet ő jelölés a « » jelek közé írt név, de újabb grafikus ikonizált forma is létezik, pl.: Sztereótípus Sztereótípus ikon ikon «capsule» «capsule» aCapsuleClass aCapsuleClass 2002, Beszédes Árpád és Ferenc Rudolf aCapsuleClass aCapsuleClass Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 35 OCL. Az OCL (Object Constraint Language) egy olyan nyelv, amely segítségével további szemantikai értelmezéssel lehet finomítani a modellt. Maga a nyelv egy szöveges leírást tesz lehetővé, amely meglehetősen kifinomult kapcsolatokat is definiálhat az egyes
modellezési elemek között. Ugyanakkor a nyelv strukturált leírást biztosít, amely a szoftver eszközök általi automatikus felismerést is elősegíti. Pl a UML metamodell szemantikájának leírásához is az OCL-t használják. Az OCL-t a UML specifikáció definiálja Egyébként, a megszorítás általános jelölése a { } jelek között történik, általában megjegyzés dobozkák részeként. Példa arra, hogy egy természetes nyelven megfogalmazott megszorítás hogyan néz ki OCL-ben: „Ha egy osztály konkrét (nem absztrakt), annak minden operációjának kell hogy legyen megvalósítása.” not self.isAbstract implies self.allOperations-> forAll (op | self.allMethods-> exists (m | m.specification-> includes(op))) Címke-érték (tagged value). Címke-érték párok segítségével úgy bővíthetjük a UML nyelvet, hogy az egyes modellezési elemekhez további adatokat, tulajdonságokat rendelhetünk. Ezt úgy tehetjük, hogy a címkével megnevezünk
egy új tulajdonságot a metamodellben, majd a modellünkben ezen címkékhez értéket rendelhetünk hozzá. Legtöbb esetben a tagged value sztereótípusokat kiegészítve alkalmazandó, annak további attribútumaként megjelenítve. Jelölése, pl.: “staus = unit tested” Az alábbi ábrán láthatjuk mindhárom bővíthetőségi mechanizmus jelölését: Profile-ok. A fenti három bővíthetőségi mechanizmust felhasználva, azok egy kombinációjával a UML-nek nevesített bővítéseit hozhatjuk létre, ezek a profileok. A profile általában egy alkalmazási terület, domain vagy valamilyen speciális környezet, vagy felhasználási minta rögzítésére szolgál. A UML (pontosabban, az OMG) néhány előredefiniált profile-t rögzít, ezek közül a legfontosabbak: üzleti alkalmazásokhoz, valósidejű, CORBA, stb. 2.48 A UML jövője A UML nyelv folyamatos fejlesztés alatt áll. Az OMG szervezetnek külön munkacsoportjai vannak az egyes fejlesztési
tevékenységekre, így a jelenlegi verziók javításaira, illetve az új verziók kiadására. A tizedessel jelölt verziónövelések főleg kisebb-nagyobb javításokat és bővítéseket jelentenek, viszont már tervezés alatt van a 2.0 UML verzió, amely a specifikáció teljes átdolgozását és modernizálását ígéri (nem tudni mikorra lesz meg). Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 36 2.4 Unified Modeling Language A másik aktuális fejlesztési irány az ún. „akció-szemantikával” bővített UML (UML with Action Semantics), amely radikális előrelépést jelenthet a vizuális modellezés terén. Itt ugyanis, hosszútávon az a cél, hogy olyan nyelvet fejlesszenek ki, amely lehetővé teszi, hogy a modell teljes egészében specifikálja a rendszert és a UML terv önállóan is végrehajtható legyen! Ez azt jelenti, hogy programozási nyelvben nem is kellene implementálni a modellt és egyéb
külső dokumentációra se lenne szükség, mert az akciók révén az már tartalmazna minden funkcionális és procedurális tevékenység leírását. Ez az elképzelés eléggé merész, és jelenleg még nem látszik, hogy megvalósítható-e. Mindenesetre, vázlat már van hozzá, az akciók leírására már nyelvek is vannak (ASL, AL) és a 2002 év végére ígérik az első verziót. Ha az eredeti cél nem is lesz megvalósítva, az akciószemantika minden bizonnyal jelentős mértékben fogja növelni a UML kifejezőképességét. 2.49 Eszközök Mint ahogy a bevezetőben már említettük, számos szoftver eszköz van, amely támogatja a UML nyelvet. Ezek között vannak speciálisan vizuális modellezésre alkalmas eszközök, mások egy teljes CASE részét képezik, míg fellelhetők egyéb jellegű szoftverek kiegészítő szolgáltatásaként a UML támogatás is (pl. általános rajzolóprogram UML elemekkel). A Rational cég Rose nevű eszköze az egyik
legelterjedtebb és az iparban széles körben alkalmazott eszköz, amely UML támogatásával a teljes szoftverfejlesztési folyamat támogatható. A Rational Rose vizuális modellezését teszi lehet ővé különféle típusú rendszerekhez, pl. kliens/szerver-, osztott-, valósidejű-rendszerek Az aktuális verzió a Rose 2001, de van demo, illetve student verzió is ingyenes használatra. A Rose számos beépített támogatást tartalmaz a legkülönfélébb objektumorientált nyelvekhez és technológiákhoz, pl.: C++, Java, DDL, IDL, WinDNA, MFC, COM, ATL, CORBA, XML/DTD, XMI, Oracle 8, stb. A Rose motor egy egyszerűsített változata a Microsoft Visual Studio 6.0 részeként is fellelhető (Microsoft Visual Modeler). 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 37 2.5 A „kurzusfelvétel” példaprojekt 2.51 A szoftverrendszer modellezésének áttekintése Ebben a fejezetben
egy elképzelt felsőoktatási intézmény kurzusfelvételi szoftverrendszer modellezését tekintjük át. A példaprojekt nem lesz teljesen kidolgozva, célja a UML használatának bemutatása. A modell kidolgozását a RUP folyamat elvei alapján végezzük, bár nem követve szigorúan annak lépéseit. A folyamat szerinti iterációkat nem követjük végig, hanem kiragadva egy-egy példát megismerjük a UML jelöléseit olyan sorrendben, amelyet egy-egy iteráción belül általában alkalmaznak. A következő tizenegy lépés szerint haladunk: • projekt megkezdése, • használati esetek modellezése, • osztályok felfedése, • kölcsönhatás diagramok, • kapcsolatok megadása, • viselkedés és szerkezet hozzáadása, • öröklődések, • objektum viselkedése, • modell végegesítés, • rendszer architektúra, Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 38 2.5 • 2.52 A
„kurzusfelvétel” példaprojekt folyamat iterációi. Projekt megkezdése Mint ismeretes, a fejlesztési folyamat kezdeti fázisának legfontosabb kérdése: „Vajon tényleg ezt a rendszert kell elkészíteni?” Ez a nulladik iterációnak a feladat. Ezt a kérdést általában bonyolult megválaszolni, hiszen a fejlesztés minden szereplőjét érinti: ügyfél, külső szakértők, szoftverfejlesztők. Sokszor prototípusokat készítenek el a koncepciók helyességének bizonyítására. Egyéb üzleti-, technológiai-, erőforrás-, stb. meggondolások is szükségesek lehetnek A „Kurzusfelvétel” alkalmazás. Feltesszük, hogy a megrendelő a következő igénnyel fordul hozzánk. Jelenleg a kurzusok kiosztása oktatóknak és a kurzusok felvétele hallgatók által következőképpen történik: 2.53 • Az oktatók leadják a tanulmányi osztályon a meghirdetett kurzusait. • A hallgatók ezen lista alapján jelentkeznek a kurzusokra (nyomtatványokon). •
A tanulmányi osztályon számítógéppel hozzárendeli a hallgatókat a kurzusokhoz, utólagos egyeztetés lehetséges, ha nem sikerült. • Jelenleg kézzel és egy központi számítógéppel történik. • Szükség van az áttérésre egy teljesen „online” megoldásra. • Nem a teljes hallgatói nyilvántartás megvalósítása a cél, hanem csak a kurzusfelvételé. • Ezután az új alkalmazást kicsit pontosabban specifikáljuk: • Minden félév elején a hallgatók megkapják a meghirdetett kurzusok részletes listáját. • Négy kurzust és mindegyikhez két alternatívát lehet választani telítettség esetére. • A kurzusokat 3–10 hallgató esetében indítják. • Az oktatók meghirdetik a kurzusaikat és lekérhetik a feliratkozott diákok listáját. • A hallgatók a kurzusfelvételi időszakban módosíthatják a választásukat. • A hallgatók tandíjat fizetnek a választott kurzusok alapján. Használati Esettanulmány A
fenti rövid specifikáció alapján el lehet kezdeni a modellezést a használati esettanulmány elkészítésével. A használati eset modellezés elveit követjük, miközben az architektúra használati eset nézetét készítjük el (Use Case View). Mint ismeretes, a használati eset modellezéskor a rendszer viselkedését modellezzük a funkcionalitás szempontjából. Ez a legmagasabb absztrakciót jelenti és jó kommunikációs eszközt képez (a felhasználó felé is). Megvilágítja rendszer tervezett funkcióit (használati eseteit), a rendszer környezetét (szereplők – Actor) és az ezek közötti kapcsolatokat (ebből lesz a használati eset diagram – Use Case Diagram). 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 39 Szereplők (aktorok). Az szerplők nem részei a rendszernek, akárki vagy akármi lehet, ami a rendszerrel kapcsolatban van. A szereplő továbbít
és/vagy fogad információt a rendszer felé, -től. A szereplőket óvatosan kell megválasztani, hiszen számos buktató lehet, és ezek kihatással vannak a teljes későbbi modellezés sikerére. A következő szempontokat érdemes figyelembe venni: • Funkcionalitások mérlegelésével kell dönteni. • Látszólag azonos szereplőkből kettő különbözőképpen használják. • Nem kell bevezetni új szereplőt, ha az általa használt funkcionalitásokat más szereplők már használják. • Iteratív folyamat eredménye. lesz, ha a rendszert teljesen Student A „Kurzusfelvétel” szereplőit a következőképpen választjuk ki: • Hallgató (Student). Személy, aki az egyetemen órát választ • Oktató (Professor). Személy, aki az egyetemen órát hirdet meg • Tanulmányi Osztály (Registrar). A Kurzusfelvétel rendszer adatainak karbantartója. • Számlázó Rendszer (Billing System). Külső információs rendszer, amely a hallgatók
felé a számlázásért felelős. Használati Esetek. A használati esetek képezik a használati eset modellezés azon elemeit, amellyel a rendszer funkcionalitását írjuk le. Nagyon fontos, hogy ez a funkcionalitás úgy legyen megfogalmazva, ahogy az a szereplő számára, azaz kívülről látható, (tehát nem a működés megvalósulását írjuk le). Valójában a használati eset nem más, mint a szereplő és rendszer közötti „párbeszéd”. Ha a használati eseteket sikerült jól megválasztani, akkor egy legfelsőbb szintű diagram használati eseteinek összessége megadja a rendszer összes működési módját, se többet, se kevesebbet. Register for courses A használati eset megválasztása általában nem könnyű feladat, kérdéses szokott lenni pl. hogy milyen részletességű funkcionalitásból legyen egy-egy új használati eset. Általában úgy célszerű ezeket megválasztani, hogy nagyobb funkcionalitást foglaljanak magukba, amelyek egy kerek
egészet alkotnak. Hasonló, közös részeket tartalmazó esetekre van megoldás a használati eset kapcsolatok segítségével. A „Kurzusfelvétel” felső szintű használati esetei a következők: • Kurzus választása. • Kurzus meghirdetése. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 40 2.5 A „kurzusfelvétel” példaprojekt • Hallgatók névsorának lekérése. • Kurzusok adatainak kezelése. • Oktatók adatainak kezelése. • Hallgatók adatainak kezelése. • Kurzuslista készítése. Használati esetek dokumentációja. A teljes fejlesztési folyamat során, de a használati eset modellezésnél különösképpen fontos a részletes dokumentáció. Mivel kevés technikai részlet elérhető ebben a fázisban, a konkrét funkcionalitások dokumentálására természetes nyelven történő leírás tartozik (amely általában külső dokumentum, pl. Word) Ide azt kell leírni, hogy mit
csinál a rendszer és nem azt, hogy hogyan. A dokumentáció során nagy segítség az ún „eseményáramlás” (Flow of Events) alkalmazása, amellyel egy-egy használati eset részleteit adjuk meg: • használati eset kezdete és vége, • adatok áramlása, • események pontos sorrendje, • egyéb feltételek, kivételes viselkedés leírása, Példaként projektünk Kurzus meghirdetése használati esetének eseményáramlását adjuk meg: • Oktató belép a rendszerbe jelszó megadásával. • A rendszer megkérdezi az oktatótól, hogy melyik félévre meghirdetett órákat kezeljen. • Menürendszer segítségével választhat: add, delete, review, print, quit. • Összes bevitelnél adat helyességét ellenőrzi a rendszer. • stb. Használati eset kapcsolatok. A használati eset diagramokon a következő kapcsolatok használhatók: asszociáció szereplő–használati eset között, kibővítés, magábanfoglalás és általánosítás
kapcsolatok használati eset–használati eset között. Ritka esetekben szereplők között is használható az általánosítás kapcsolat Az alábbi ábrán láthatunk példát ezen kapcsolatokra: 1 * <<include>> Az asszociációs kapcsolat valójában egy kommunikációs kapcsolatot jelent, amely a szereplő és a használati eset kommunikációját írja le («communicates» 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 41 sztereótípussal ellátott asszociáció). A kapcsolat lehet irányítás nélküli, ez esetben kétirányú kommunikációt jelent, ellenkező esetben viszont az irány a kezdeményezés irányát adja meg. Multiplicitás is megadható Amikor két használati eset közé adunk meg kapcsolatot, akkor az kétféle specialitás függőségi (dependency) kapcsolat lehet, sztereótípusokkal megadva: „magábanfoglalja” és „bővíti” («include»
és «extend»). A „magábanfoglalja” esetében részfunkcionalitást ábrázolunk, amikor az egyik (rész)funkció többször is szerepel. Ez azt jelenti, hogy a részfunkcionalitás minden esetben végre hajtandó A „bővíti” esetében viszont, valamilyen speciális funkcionalitással történő kibővítést írunk le (pl. kivételes viselkedés) és esetleg opcionális végrehajtást is jelenthet. Az általánosítás a szokásos szemantikai általános/speciális viszonyt fejezi ki. Használati eset diagramok. A használati esetekből és szereplőkből felépített diagramok a modell olyan nézeteit adják meg, amelyek vagy a rendszer áttekintését szolgálják (fő diagram), vagy az egyes szereplő és/vagy funkcionalitások szemszögéből vizsgálják. Amikor szükséges, a rendszer határát is meg kell rajzolni (téglalappal bekeretezni), pl. a fő diagramon A „Kurzusfelvétel” fő használati eset diagramját az alábbi ábrán láthatjuk: Professor Select
courses to teach Student Register for courses Request course roster Billing System Registrar Create course catalogue Maintain student information Maintain course information Maintain professor information Az alábbi ábra egy segéd használati eset diagramot mutat, amely az oktató szempontjából ábrázolja a rendszert, kicsit részletesebben feltüntetve a használati eseteket: Professor Select courses to teach Request course roster <<include>> <<include>> Validate user Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 42 2.54 2.5 A „kurzusfelvétel” példaprojekt Osztályok azonosítása A kezdeti használati eset diagramok és a hozzá tartozó dokumentációból kiindulva elkezdhetjük megtervezni a rendszerünk logikai szerkezetét, azaz a kezdeti osztálydiagramokat. Először is, beazonosítjuk a főbb objektumokat, majd a hozzájuk tartozó osztályokat, amelyeket sztereótípusokkal
látunk el és csomagokba csoportosítunk. Az osztályok beazonosítás sokszor triviálisnak tűnik, de nagyobb rendszerek esetében ritkán egyértelmű az első ránézésre, hogy milyen egységekből legyen egy-egy osztály. Ilyen esetben mindig abból induljunk ki, hogy mik azok az objektumok, amelyek a rendszer futásakor képződhetnek. Ezt is általában az intuíciónk szerint végezzük, de nem árt, ha jobban megértjük, mi is rejlik a megérzéseink mögött. Mint ahogy azt az OOP alapjaiból ismerjük, egy objektum olyan entitás (valós világból vagy elvonatkoztatott), rendelkezik: • állapottal (attribútumok határozzák meg), • viselkedéssel (operációk határozzák meg és annak módját írja le, hogyan reagál más objektumok kéréseire) és • identitással. Egy objektum identitása annyit jelent, hogy minden objektumi egyedi, még akkor is, ha az állapotuk azonos. Az identitás nyilván úgy valósul meg, hogy a minden objektumnak különálló
memóriaterület van fenntartva (eltekintve a megosztott objektumoktól, de az más lapra tartozik). Az objektum UML szerinti jelölése: Algebra A fentiek figyelembevételével először próbáljuk felfedezni, hogy milyen objektumok létezhetnek az alkalmazásunkban, majd az azonos állapottal és viselkedéssel (más objektumok felé, ill. belső szemantika) rendelkezőknek hozzunk létre osztályokat. (Itt jegyezzük meg, hogy az öröklődéssel megvalósítható általánosítást egy későbbi fázisban határozzuk meg, amikor már több ismeretünk van.) Ebben a fázisban is nagyon fontos az egyes osztályok dokumentálása, aminek az a lényege, hogy az osztály szerepét írjuk le, és nem a szerkezetét. Pl az nem jó dokumentáció, hogy a CourseOffering osztály „egy egészet és egy sztringet tartalmazó osztály”. Ennél többet mond az, hogy „a Kurzusfelvétel alkalmazásban a meghirdetett kurzus adatainak tárolására és kezelésére szolgál”. Konkrétan ez
az osztály attribútumai a tanterem és időpont, operációi pedig a tanterem és időpont lekérése, új hallgató hozzáadása, stb. Figyelem: itt még nem adunk meg konkrét típusokat, szignatúrákat, csak a lényegi működés elvét! Ezt az osztályt UML szerinti jelöléssel a következőképpen ábrázolhatnánk: <<entity>> CourseOffering (f rom Univ ersity Artif act s) getOffering() addProfessor() CourseOffering (from UniversityArtifacts) A második jelölésmód a speciális sztereótípus szerinti ikonizált forma. A fenti Algebra nevű objektum e osztály egy példánya. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 43 A fenti példában az osztály sztereótípusa az entity volt, amely egy speciális előre definiált osztály-fajta. A kezdeti fázisban nagy segítség lehet azonnal alkalmazni néhány tipikus sztereótípust, mert általában nehéz feladat.
Maga a RUP folyamat ad egy javaslatot is arra nézve, hogy milyen sorrendben célszerű elindulni (boundary-val kezdjük, majd control, végül entity). Az alábbi alapvető osztály sztereótípusokat használhatjuk: • «Entity». Entitás osztályok modellezésére, általában hosszú életű információt, viselkedést tartalmaz. Ezek az osztályok a valós világ entitásai, amelyek kevésbé érzékenyek a környezetük változásaira. Sok esetben más alkalmazásokban is felhasználhatók. Felfedezésükhöz jó kiindulás lehet a használati eset leírásokban a feladatkiosztások főnevei. Pl: személyek adatai, kurzus adatai, stb. • «Boundary» (határ). Ezen osztályok a rendszer környezete és belseje közötti kommunikációt valósítják meg és valójában interfészt képeznek a felhasználó vagy más rendszer (szereplő) felé. Pl a felhasználói interfész is ide tartozik Jelen példánkban a „kurzus meghirdetése” egy boundary osztály. •
«Control». A control (vezérlő) osztályok a használati esetek szekvenciális viselkedését valósítják meg, azaz a használati eset „végrehajtását”. Általában egy szereplő/használati eset párhoz hozzátartozik egy-egy ilyen osztály. A vezérlő osztályok megválasztása már nem olyan egyértelmű, mert a funkcionalitás az entitás osztályhoz tartozhat. Általában modulárisabb lesz a rendszerünk, új a funkcionalitás vezérlő osztályba kerül. Pl „oktató és kurzus kezelője” egy vezérlő osztály az alkalmazásunkban. • Egyéb (pl. utility, exception) Példaképpen nézzük a Kurzusfelvétel osztályait a „Kurzus meghirdetése” használati esethez (e használati eset lehetőséget ad az oktatónak, hogy válasszon egy kurzust amelyet oktatni fog). Boundary osztályok: olyan osztályokat vizsgálunk itt, amelyek csak az Oktató szereplővel vannak kapcsolatban, pl. az esettanulmányban leírtak alapján az oktató módosítani tudja az
adatokat, erre szolgáljon a ProfessorCourseOptions osztály, új kurzus hozzáadására pedig AddACourseOffering. Az entity osztályok az esettanulmány alapján könnyen beazonosíthatók: Course, CourseOffering, ProfessorInformation. Control osztályként új osztályt hozunk létre esemény áramlások kezelésére ProfessorCourseManager néven. Mint már megismertük, a csomagok (Package) használata nagy rendszereknél elkerülhetetlen az osztályok csoportosításához. Minden UML csomagban megadhatunk interface osztályokat (publikus elérésűek) és implementációs osztályokat (ezek belső, privát osztályok, amelyek a csomagon kívül nem használhatók). Az eddigi osztályain három logikai csoportba oszthatók: • Az egyetemre vonatkozó dolgok: UniversityArtifacts CourseOffering és ProfessorCourseManager osztályokkal. • Személyekre vonatkozó adatok: PeopleInfo a ProfessorInformation osztállyal. • Az esettanulmány szereplőihez kapcsolódó interfészek:
Interfaces ProfessorCourseOptions és AddACourseOffering osztályokkal. Objektumorientált nyelvek és módszertanok a Course, a 2002, Beszédes Árpád és Ferenc Rudolf 44 2.5 A „kurzusfelvétel” példaprojekt Az eddigiek alapján elkészíthetjük az első osztálydiagramjainkat. Az előbbi három csomagba osztott osztályaink a modellt képezik, amelyből különböző nézetek szerint készíthetünk osztálydiagramokat. Általában minden csomaghoz tartozik egy fő diagram (Main), valamint az egész rendszer logikai nézetéhez egy fő diagram, amely a felsőszintű csomagokat ábrázolja. A csomagok fő diagramjai általában csak a csomag publikus osztályait tartalmazzák. A fő diagramok mellet egyéb osztálydiagramokat is szokás használni, pl. csomag implementációs osztályait ábrázoljuk vagy az öröklődési hierarchia megjelenítése. A teljes diagramokhoz hozzátartoznak a kapcsolatok is, de ezeket később adjuk hozzá. Tehát a
példaalkalmazásunk fő osztálydiagramja a következő (figyeljük meg a függőségi kapcsolatot): Interfaces PeopleInfo UniversityArtifacts A UniversityArtifacts csomag fő diagramja (a CourseOffering osztály hiányzik, mert az belső implementációs osztály): Course ProfessorCourseManager Az alábbi ábrán megjelölésével: egy példát láthatunk osztálydiagramra sztereótípusok <<boundary>> AddACourseOffering (from Interfaces) <<boundary>> ProfessorCourseOptions (from Interfaces) <<control>> ProfessorCourseManager (from UniversityArtifacts) <<entity>> Professor (from PeopleInfo) <<entity>> Course (from UniversityArtifacts) <<entity>> CourseOffering (from UniversityArtifacts) 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 2.55 45 Objektumok kölcsönhatása – scenario, interaction A
következő lépésként elkezdjük vizsgálni az objektumok dinamikus viselkedését. Ehhez szükségesek a használati eset ismeretei, valamint felhasználhatjuk az előző részben beazonosított osztályainkat. Valójában a használati eset megvalósítását modellezzük az objektumok egymás közti kölcsönhatások vizsgálatával. Ehhez használni fogjuk a szcenárió fogalmát Szcenáriónak nevezzük a használati eset megvalósulását az objektumok közötti kölcsönhatások (egymásnak küldött üzenetek) leírásával, azaz a használati eset egy „példánya”, az eseményáramlás egy útvonala. Más szóval, amíg az eseményáramlás leírása természetes nyelven történik, addig ugyanezt a dolgot UML nyelv segítségével a szcenárió valósítja meg a kölcsönhatás diagramokkal, mint grafikus nézetekkel. Kétféle kölcsönhatás diagramot használhatunk: • szekvencia diagram (Sequence Diagram) és • együttműködési diagram (Collaboration
Diagram). Szekvencia diagramok. A szekvencia diagram objektum-kölcsönhatásokat mutat be az idő függvényében, a szcenárióban szereplő objektumokat és osztályokat ábrázolja a közöttük küldött üzenetekkel. A legfontosabb ismérve, hogy időorientáltan mutatja be az üzeneteket, tehát az időbeli változások jól követhetők rajta. Egy-egy szekvencia diagram mindig egy konkrét végrehajtást ábrázol, tehát sok esetben elég nagy lehet. Ezért célszerű a szcenárió kis egységeit ábrázolni egyszerre. Azonban, a UML megad néhány jelölést, amelyek segítségével vezérlési szerkezetekkel lehet bővíteni a szekvencia diagramokat, így nem csak egy végrehajtás ábrázolható, de ezek használata korlátozott. Elemei az objektumok, amelyek megnevezett objektum, megnevezett objektum + hozzátartozó osztály vagy anonymous (csak osztálynév) lehetnek. A szekvencia diagramokon szereplő üzenetek (Message) a szcenárió funkcionalitását valósítják
meg, amelyek sokszor az üzenetet fogadó objektum metódus hívását jelenti. A szekvencia diagramok a használati esetekkel szoros kapcsolatban állnak, ezért általában a Használati Eset nézetnek a részei. Példa szekvencia diagram a „Kurzusok adatainak kezelése” használati esetnek „Kurzus létrehozása” szcenáriójához (figyeljük meg, hogy itt még nem konkrét metódusnevek szerepelnek, hanem csak elvi üzenetek): a course form the manager a course : Course : Registrar set course info process add course new course Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 46 2.5 A „kurzusfelvétel” példaprojekt Itt érdemes megfigyelni, hogy az első objektum az egy külső szereplő egy példánya, ezek szerint az ő általa küldött üzenetek valószínűleg valamilyen boundary osztályhoz szólnak, jelen esetben a felhasználói felületen keresztül. Együttműködési diagramok. Az együttműködési
diagramok a szekvencia diagram mellett a szcenárió alternatív ábrázolására szolgálnak. Valójában a kettő teljesen kompatíbilis, egymásból származtathatók. Míg a szekvencia diagramok inkább a megrendelő számára nyújthatnak hasznos információt és a tervezés korai fázisaira jellemzők, addig az együttműködési diagramok a kapcsolatok implementációjánál lehetnek fontosak. Ezen diagramok az objektumok kölcsönhatásait mutatja be maguk az objektumok és kapcsolataik szemszögéből, azaz ez egy üzenet-orientált nézet. Elemei úgyszintén az objektumok, kapcsolatok objektumok között, az üzenetek nyilakkal ábrázolva. A fenti szekvencia diagramnak megfelelő együttműködési diagram: 1: set course info 2: process a course form : Registrar 3: add course the manager 4: new course a course : Course 2.56 Kapcsolatok megadása (relationships) Az eddigi ismereteinket felhasználhatjuk arra, hogy a szerkezeti modellünket tovább bővítsük
asszociációs kapcsolatok megadásával (association). Egy-egy asszociációs kapcsolat az osztályok között gyakorlatilag mindig egy objektumkölcsönhatás megvalósulása, más szóval a kapcsolatok az objektum-üzeneteknek a „csatornái”. Az asszociációs kapcsolat speciális fajtájaként az aggregációs kapcsolatokat (rész-egész kapcsolat) is felfedjük (aggregation). A kapcsolatok különböző tulajdonságokkal fogjuk kiegészíteni: név, irány, szerep, multiplicitás, stb. Ezen kívül, függőségi kapcsolatok is használhatunk gyengébb összefüggések ábrázolására. Az osztályokon kívül, csomagok között is megadhatunk bizonyos kapcsolatokat. Asszociáció kapcsolat. Az általános asszociáció kapcsolat egy osztályok közötti kétirányú összeköttetés, amelynél navigálási irány megadható az üzenet irányának jelzésére. Általában valamilyen használati kapcsolatot jelent a két osztály között, amelyek léte egymástól általában
független, de legalább az egyik ismeri és/vagy használja a másikat (az ismeretség irányát megadhatjuk a navigálási irány segítségével). A kapcsolat szemantikus összefüggést ábrázol és nem adatfolyamot (mindkét irányba lehet információ/adat továbbítás). A valódi összefüggés az osztályokból létrejövő objektumok között jön létre, amely különböző 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 47 kölcsönhatásokat jelent. UML jelölés szerint az asszociációs kapcsolat egyszerű tömör vonal a két osztály között, amelyekre különböző tulajdonságok is elhelyezhetők: manages ProfessorCourseManager 0.n Course 1.n Aggregációs kapcsolat. Ez a kapcsolat az asszociáció egy speciális formája, amely „rész-egész” viszonyt fejez ki a résztvevő osztályok között (erősebb mint az asszociáció), vagyis azt, hogy az egyik objektum
fizikailag tartalmazza vagy birtokolja a másikat. Ezt a kapcsolatot sok esetben nagyon nehéz precízen meghatározni, hiszen nem mindig egyértelmű, hogy a kapcsolat teljes vagy részleges tartalmazást jelent, vagy csak hivatkozást a másik objektumra. Egy asszociációból általában akkor célszerű aggregációt csinálni, amikor a kapcsolat leírásánál a „része” kifejezés egyértelműen használható vagy amikor az egészobjektum bizonyos műveletei automatikusan a rész-objektumokra is vonatkoznak (pl. megszűnés) UML jelölés szerint a rombusz a tartalmazó oldalán jelzi az aggregációt: Course CourseOffering 1 1.n Alapvetően kétféle aggregációt különböztetünk meg: • gyenge tartalmazás – általános aggregáció (teli rombusszal jelölve) • erős tartalmazás, vagy más néven kompozíció – akkor használjuk amikor rész(ek) élettartama szigorúan megegyezik az egészével (üres rombusz) B A C A kompozíciót általában könnyű
felismerni, pl. arról, hogy ha a tartalmazó megszűnik, akkor biztos a tartalmazottnak is meg szűnnie, vagy ha a tartalmazónak valamilyen művelet végrehajtása minden esetben maga után vonja ugyanazon művelet végrehajtását a tartalmazotton is. Az alábbi példa szemlélteti az általános asszociáció és a gyenge és erős aggregáció közti különbséget. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 48 2.5 A „kurzusfelvétel” példaprojekt Head Has Dog -pet 0.* Owns -owner Person 1 Carries 0.100 Flea Kapcsolatok nevei. Az asszociáció kapcsolatnak opcionálisan lehet neve, amely általában egy igés kifejezés (pl. Owns, Carries, Has) A név olvasásának iránya is fontos (melyik osztálytól melyik felé), ha nem egyértelmű, akkor egy kis háromszög a név mellett mutathatja meg. A nevet szenvedő módba is lehet rakni, ha a fordított irány kifejezése fontosabb (pl. Is owned by) Az
aggregációknak általában nincs nevük, mert egyébként is oda kell érteni és mindig a következőkhöz hasonló: Has, Contains, Kapcsolat végpontok. A kapcsolatok további fontos tulajdonságait a végpontokhoz rendelhetjük hozzá. Így ezen tulajdonságok az adott végpontoknál szereplő osztállyal való kapcsolatra vonatkoznak, míg a kapcsolat név az egész kapcsolatra. Az egyik végpont tulajdonság a szerepnév (role names). A szerepnév általában egy főnév, amely a kapcsolat adott végpontján levő osztály szerepét írja le az adott kapcsolatban. Az alábbi példában az oktató mint személy-entitás (Professor osztály) a feltüntetett kapcsolatban tanárként szerepel (Teacher), tehát a szerepnév további pontosítása az osztálynak az adott kapcsolat szempontjából: <<entity>> CourseOffering (from Uni versityArtifacts) 0.4 +Teacher<<entity>> Professor 1 Sok esetben nem szükséges egyszerre kapcsolat nevet és szerepnevet is
megadni. A szerepnév általában helyettesítheti a kapcsolat nevét, hiszen kifejezőbb és pontosabb lehet annál. Másfelől viszont, csak akkor használjunk kapcsolat nevet ha az szükséges az érthetőség miatt. Az alábbi példában a két szerepnév fölösleges, hiszen a kapcsolat nevéből egyértelműek a szerepek, és fölöslegesen nem kell hizlalnunk a diagramunkat. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 49 Person Employs Employer +the employer Employee +employed person Ezek nem kellenek! Multiplicitás. Multiplicitást a kapcsolatok végpontjainál definiálhatunk a hozzá tartozó osztályokhoz annak érdekében, hogy a kapcsolat megvalósulásánál résztvevő objektumoknak számát pontosítsuk. Példák a megadásra: • 1 – pontosan egy • 0.* (vagy ) – nulla vagy több (akárhány) • 1.* – egy vagy több • 0.1 – nulla vagy egy •
5.8,11 – 5, 6, 7, 8 vagy 11 A fenti példában azt ábrázoltuk a multiplicitások segítségével, hogy minden oktató 0–4 kurzust hirdethet meg és hogy minden kurzust pontosan egy oktató hirdethet meg. Reflexív kapcsolatok. Kapcsolat természetesen létesíthetünk egyazon osztályból kiindulva saját magával is, hiszen az azonos osztályhoz tartozó különböző objektum példányok egymással is kommunikálhatnak. Az osztálydiagramon ezek a reflexív asszociációk és aggregációk. A reflexív kapcsolatoknál általában a szerepek vannak megnevezve és nem maga a kapcsolat. Kapcsolatok keresése. Ahhoz hogy a kapcsolatokat sikeresen beazonosítsuk, a szcenáriókat kell vizsgálni, hiszen a kölcsönhatási diagramokon az objektumok közötti üzenetek mindenféleképpen valamilyen osztályok közötti kapcsolatot sejtetnek. Általában biztos út az, ha első közelítésben asszociációt hozunk létre minden kölcsönhatásra, majd ha erre a kapcsolatra ráillik
az, hogy „része” vagy „tartalmazza”, akkor az legyen inkább aggregáció. Kapcsolat létrejöhet egyéb megfontolásból is, mint. pl öröklődések tervezésekor (ld később) A Kurzusfelvétel alkalmazásban a fent megadott „Kurzus meghirdetése” használati esetnek „Új kurzus hozzáadása” szcenáriója alapján a következő üzeneteket lehet felismerni: • ProfessorCourseOptions AddACourseOffering • AddACourseOffering ProfessorCourseManager • ProfessorCourseManager Course • Course CourseOffering Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 50 2.5 A „kurzusfelvétel” példaprojekt Ezen üzenetek és egyéb, használati esettanulmányban leírtak (pl. Kurzus előfeltétele), alapján a következő osztálydiagramot adhatjuk meg most már a kapcsolatok feltüntetésével: ProfessorCourseOptions ProfessorCourseManager (from Interfaces) 1 0.n 1 manages 1.n +Pre-requisite Course 1
AddACourseOffering1 (from Interfaces) 1 1 0.n 1 0.n 1.n CourseOffering Kapcsolatok csomagok között. Korábban már csoportosítottuk a szerkezeti modellünket csomagokba. A jelenlegi kapcsolatok alapján a csomagok között is létrehozhatunk kapcsolatokat. A csomagok közötti kapcsolat típusa a függőségi kapcsolat (Dependency Relationship), amelyet szaggatott nyíllal jelölünk. Függőség akkor van, ha a függő csomag (Client) legalább egyik osztálya kommunikációt kezdeményez a másik csomag (Supplier) valamely publikus osztályával („használja” azt), azaz: Package A Client Package B Supplier A jelenlegi tudás alapján az „Új kurzus hozzáadása” szcenárióban az AddACourseOffering osztály üzenetet küld a ProfessorCourseManager osztálynak és emiatt hozunk létre egy függőségi kapcsolatot az Interfaces és a UniversityArtifacts csomagok között (ld. fent) 2.57 Viselkedés és struktúra Az eddigiek alapján már elegendő információval
rendelkezünk ahhoz, hogy az osztályainkat pontosítsuk a viselkedés (operációk) és a struktúra (attribútumok) hozzáadásával. Valójában ezen két tulajdonsággal együtt lesz teljes egy osztály Az objektumok viselkedését a hozzátartozó osztályok foglalják magukba, és az operációk (metódusok) határozzák meg. Az operációk megválasztásakor ügyeljünk arra, hogy egy operáció csak egy valamit csináljon, de azt az egyet jól tegye! Pl. a CourseOffering osztálynak képesnek kell lennie felvenni és törölni egy hallgatót. Az elnevezésekre vonatkozóan célszerű valamilyen konvenciót követni, pl. hogy kisbetűvel kezdődjenek, összetett szónál minden szó kezdődjön nagybetűvel. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 51 Az objektumok struktúrája a hozzátartozó osztályok attribútumaival van leírva. Valójában minden attribútum egy
adatdefinició, amit az osztály objektumai tárolnak. Pl a Course osztály attribútumai: név, leírás és kreditpontok (figyeljük meg, hogy itt még mindig nem használunk teljes leírást, a típusok pl. hiányoznak) Az elnevezésre az operációknál leírtakat elkalmazhatjuk, bár mást is szoktak használni, főleg privát adattagok esetén (pl. aláhúzás karakterrel keződik) FONTOS: Nagyon fontos megjegyezni, hogy annak ellenére, hogy a kapcsolatok implementációja sok esetben az osztály valamilyen adattagjával van megvalósítva (pl. pointer a másik osztályra), az ilyen adattagokat nem kell felvenni attribútumnak! Tehát, csak olyan attribútumnak van értelme, amelyik tényleg valamilyen adatot hordoz, struktúrát határoz meg, a kapcsolatok megvalósításai nem attribútumok. Operációk létrehozása. A kölcsönhatás diagramokban levő üzenetek tipikusan leképezhetők a fogadó osztály operációira. Ez jó kiindulási pont lehet, bár korántsem fedi le
az összes szükséges oparációt. Ez alól kivételt képez az, ha az üzenetet fogadó osztály boundary típusú (határosztály) és ez valamilyen felhasználói felület megvalósítása. Pl az „új kurzus hozzáadása” szcenárióban az oktató szereplőnek meg kell adnia a jelszót. Ez az üzenet nyilván nem a ProfessorCourseOptions operációja lesz, hanem egy szövegmező az ablakon. Hasonlóan, ha az üzenet egy olyan szereplőtől jön (szereplőhöz megy), aki fizikailag egy személy, akkor nem lesz operáció, hanem pl. a felhasználói kézikönyvbe kerül. Kölcsönhatás diagramoktól függetlenül is létre lehet hozni operácókat, ilyenek pl. a segéd operációk Az alábbi példa mutatja „Kurzus meghirdetése” használati esetnek „Új kurzus hozzáadása” szcenáriójához tartozó szekvencia diagramot most már konkrét metódus hívásokkal, mint üzetekekkel: : Course : AddACourseOffering : Professor: ProfessorCourseOptions : Profess orCours
eManager : CourseOffering enter password verify password enter semester add an offering display select Math 101 getOfferings(Course) getOfferings( ) getOffering( ) display offerings select offering setProfessor(Profes sor, Course, CourseOffering) setProfessor(Profes sor, CourseOffering) addProfess or(Professor) Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 52 2.5 A „kurzusfelvétel” példaprojekt Az operációk dokumentálása is nagyon fontos. Itt arra kell törekedni, hogy azt írjuk le hogy az adott operáció mire való, azaz a funkcionalitását magyarázzuk el (tehát ne azt írjuk le, hogy hogyan valósítja ezt meg). A bemenő- és kimenő paramétereket is meg kell adni, így kapjuk meg az operáció szignatúráját (lenyomat). A teljes szignatúra megadható a tervezés későbbi fázisában is, itt elegendő dokumentálni a paramétereket. Az operáció szignatúrája sok esetben valamilyen további kapcsolatot is
reprezentálhat. Pl a Course osztályon belüli addProfessor operáció bemenő paraméterei a professor és a courseOffering. Ebből következően kapcsolatok léteznek a: Course és ProfessorInformation, valamint a Course és CourseOffering osztályok között. Kezdetben ezeket felvehetjük asszociációnak, de később lehet függőség (dependency) is, ha kiderül hogy ezek a kapcsolatok csak ideiglenes és/vagy gyengébb függőséget jelképeznek. Attribútumok létrehozása. Sok attribútum megtalálható a probléma leírásban, a követelmény specifikációban és az adatfolyam dokumentációban. Ezekből az attribútumok nagy része különösebb nehézség nélkül meghatározható (ezért is fontos ezeket a korai dokumentumokat alaposan elkészíteni). Pl a követelmények közt szerepel, hogy a kurzus megnevezése, leírása és a kreditpontok elérhetőek a meghirdetett kurzusokon. Ebből következően a név, leírás és a kreditpontok a Course osztály attribútumai
lesznek. A dokumentálásnál itt is arra ügyeljünk, hogy azt írjuk le, hogy az attribútum mire való és nem azt, hogy hogyan épül fel. Operációk és attribútumok megjelenítése. Az osztálydiagram szolgál az operációk és attribútumok megjelenítésére, sőt, sokszor csak erre hozzuk létre kapcsolatok megjelenítése nélkül, pl.: <<entity>> CourseOffering getOffering() addProfessor() <<entity>> Course name description creditHours <<control>> ProfessorCourseManager getOfferings() setProfessor() getOfferings() setProfessor() Asszociációs osztályok. Eddig megismerkedtünk a kapcsolatok alapvető tulajdonságaival. Viszont, lehetnek esetek, amikor többre van szükség, azaz a kapcsolatnak is lehet viselkedése és struktúrája. Pl minden hallgató osztályzatot kap. Ez hol legyen tárolva? Nem tartozik a hallgatóhoz, mert ő különböző tárgyakhoz különböző osztályzatokat kap. A kurzushoz sem tartozik, mert
különböző hallgatókhoz különböző osztályzatok vannak. A megoldás az asszociációs osztály használata, hiszen az osztályzat a két osztály közötti kapcsolathoz tartozik. UML szerinti ábrázolása a következő: 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 53 CourseOffering Student (from UniversityArtifacts) 0.4 3.10 1 1 ReportCard Grade 0.4 2.58 1 Öröklődések feltárása Az általánosítás kapcsolat (Generalization) a UML-ben egy nagyon sokrétűen alkalmazható kapcsolat. Osztály diagramok esetében az osztályok közötti öröklődés (Inheritance) ábrázolására használatos, amikor ugyanis az egyik osztály megosztja a struktúráját és/vagy a viselkedését egy vagy több másik osztállyal. Így egy öröklődési hierarchiát kapunk, ahol a származtatott osztály örököl az ősosztály(ok)tól (subclass derives from superclass). A UML
értelmezése szerint, a származtatott (gyerek) osztály mindent örököl az őstől, beleértve a relációkat is. A UML általánosítás kapcsolatnak nincs neve, nincsenek használva szerep nevek és multiplicitás sem. Jele: A B Öröklődés feltárása. Ha már van egy csonka osztálymodellünk, amelyben osztályok már szerepelnek, a hozzájuk tartozó attribútumokkal, operációkkal és kapcsolatokkal, az öröklődések feltárása sokkal könnyebb mintha azzal kezdtük volna a tervezést. Két egyszerű elvet kell követnünk és a meglévő osztályokban keresnünk ahhoz, hogy előálljon az öröklődési hierarchia: • Általánosítás. Általánosításkor több osztály közös rész-struktúráját és viselkedését egy közös ősosztályba helyezzük Ez a tevékenység a tervezés kezdeti szakaszaira jellemző (legelőször a valós világot írjuk le majd később vonatkoztatunk el), tehát így célszerű kezdeni az öröklődések feltárását.
Általános szabály, hogy az attribútumok, operációk és kapcsolatok a lehető legmagasabb szintre kerüljenek a hierarchiában! Pl. a StudentInformation és ProfessorInformation osztályoknak is vannak név, cím és telefonszám attribútumai, ezért kézenfekvő ezeknek egy közös ősosztályt létrehozni: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 54 2.5 A „kurzusfelvétel” példaprojekt RegistrationUser Professor • Student Specializálás. Specializáláskor származtatott osztályokat hozunk létre, amelyek finomítják az ősosztályt (tipikusan új struktúrát és viselkedést adnak hozzá). Pl a beiratkozási rendszert bővíthetnénk, hogy az idősebb hallgatók (nyugdíjasok!?) ingyen vehessenek fel kurzusokat. Ilyenkor új attribútumok vezethetünk be, pl. hallgató életkor, vagy új operációkat, pl kurzus ingyen felvétele. Általánosítás és a kapcsolatok. Míg az attribútumok és
operációk helye többékevésbé egyértelmű, a kapcsolatok helyére az általánosítás után két alapvető lehetőség van: • Maradnak a származtatott osztályok szintjén: RegistrationUser name IDNumber Student major Professor tenureStatus 1 3.10 +Teacher 0.4 0.4 CourseOffering (from UniversityArtifacts) • Ősosztály szintjén új kapcsolatot alakítunk ki módosított szerepkkel és multiplicitással. Pl ami magában foglalja az oktató és a hallgató objektumok összegét (vagyis egy CourseOffering objektum 4–11 UserInformation objektummal álljon relációban). Itt viszont további megszorításokra is szükség lehet, hiszen azzal hogy följebb került a kapcsolat, elvesztek bizonyos információink. Pl. egyik UserInformation biztos ProfessorInformation kell hogy legyen. A két megoldás közül sokszor nehéz dönteni, ilyenkor mindig a közérthetőségre és egyszerűségre törekedve döntsünk. 2002, Beszédes Árpád és Ferenc Rudolf
Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 55 Többszörös öröklődés. Többszörös öröklődésről beszélünk akkor, ha egy osztálynak több ősosztály-lánca van, azaz ugyanahhoz az őshöz több mint egy úton juthatunk el a hierarchiában (irányítatlan értelemben kör van benne). Pl a kétéltű jármű egy motorgépkocsi, ami egy jármű és az egy vízi jármű is ami ugyancsak egy jármű: Jármû Motorgépkocsi Vízi jármû Kétéltû jármû Ilyen többszörös öröklődéseket tartalmazó hierarchiák kezelésekor számos probléma adódhat, pl. név ütközések, többszörösen örökölt operációk/attribútumok, stb. Éppen ezért, számos programozási nyelv nem is támogatja a többszörös öröklődést (pl. Java) Egyéb nyelveken természetesen megoldható a probléma és kezelhető is, de csak nagy odafigyeléssel (pl. C++-ban virtuális öröklődéssel). Mindenesetre, kevésbé karbantartható
kódhoz vezet ezért kerüljük a használatát. Öröklődés és aggregáció. Mind az öröklődés, mind az aggregáció valamilyen módon az objektumok összetételét segítik. Sok esetben felmerül tehát a kérdés, hogy melyiket célszerű alkalmazni. Hajlamosak vagyunk arra, hogy túl sok öröklődést használjunk, pedig az újrafelhasználhatóságnak és karbantarthatóságnak a sok öröklődés csak árthat. Az öröklődést sokszor rosszul használják, pl. egy hallgató lehet nappali vagy levelező, ezért származtathatnánk két osztályt a StudentInformation-ból. Ebből viszont problémák adódhatnak: • ha a nappalis hallgató áttér levelező tagozatra, meg kell változtatni az objektum osztályát (ami általában nem célszerű), • újabb specializálásra van szükség ami többszörös öröklődéshez vezet. Általános szabály, hogy az öröklődést kell használni a közös elválasztására a specifikustól, míg az aggregáció a
tartalmazások ábrázolására szolgál. Ezért mindig ezen alapvető szempontokat vegyük figyelembe, azaz az osztályok elvi szerepét figyeljük, és ne azt, hogy megvalósítás szintjén melyik lenne célszerűbb! A fenti levelezős hallgató problémáját a következőképpen oldhatjuk meg az öröklődés elkerülésével: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 56 2.5 A „kurzusfelvétel” példaprojekt <<entity>> RegistrationUser name IDNumber <<entity>> Professor tenureStatus <<entity>> Student major 1 <<entity>> Classification 0.n 1 3.10 +Teacher 0.4 0.4 <<entity>> CourseOffering Fulltime Parttime (from UniversityArtifacts) 2.59 Dinamikus viselkedés Az utolsó nagyobb logikai modellezési részben a viselkedési modelleket pontosítjuk. Az eddigi dinamikus viselkedési modellezés az objektumok között kölcsönhatásokra korlátozódott
a használati esetek és szcenáriók segítségével. Viszont, miután már megvannak az osztályok részletesebb specifikációi (attribútumok, operációk, kapcsolatok), elkezdhetjük az egyes osztályok viselkedésének finomítását. Ehhez az objektumok dinamikus viselkedését elemezzük és modellezzük az állapotátmeneti diagramok segítségével. Állapotátmeneti diagramok. Az állapotátmenet diagram egy konkrét objektum állapotait ábrázolja, az eseményeket és üzeneteket, amik az állapotváltásokat kiváltják, és az akciókat, amik végrehajtódnak állapotváltáskor. Az állapotátmeneti diagram magába foglalja az összes üzenetet, amit egy objektum küldhet, illetve fogadhat. Csak jelentős dinamikus viselkedéssel bíró osztályokhoz célszerű ezeket létrehozni (amik sok üzenetet fogadnak és/vagy küldenek). Az állapotátmeneti diagramokat legtöbb esetben a kölcsönhatás diagramok (szekvencia, kollaborációs) alapján származtatják azáltal,
hogy a küldött és fogadott üzeneteket vizsgálják: pl. a szekvencia diagramban két üzenet közötti intervallum általában egy állapotként reprezentálható. Azonban sok esetben szükség lehet visszahivatkozni a használati esettanulmányhoz is: a szcenáriók pl. egy konkrét utat reprezentálnak az állapotátmenet diagramban. Egy jól kidolgozott állapotátmeneti diagram az osztályhoz elegendő specifikációt ahhoz, hogy közvetlenül implementálható legyen a funkcionalitása. Állapotok (state). Egy objektum állapotait az osztályának egy vagy több attribútuma határozza meg, pl. egy CourseOffering objektum lehet nyitott vagy zárt. Ezen kívül, egy hivatkozás létezése egy másik objektumra is meghatározhatja az állapotot. Tehát egy objektum állapotait az attribútumainak és hivatkozásainak vizsgálatával határozhatjuk meg. UML jelölése az állapotnak egy lekerekített sarkú téglalap: 2002, Beszédes Árpád és Ferenc Rudolf
Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 57 Állapotátmenetek. Az állapotátmenet egy objektum állapotának megváltozását jelenti (maradhat ugyanaz is), amit egy akció kísérhet. Az állapotátmenet lehet automatikus, amikor befejeződött az egyik aktivitása, vagy nem automatikus, ha egy névvel ellátott üzenet váltja ki (másik objektumtól vagy külső). Mindkét esetben azonban az állapotátmenet időtartama nullának tekintendő és nem lehet azt megszakítani (az állapot megszakítható). UML jelölése egy nyíl az eredendő állapotból az újba. Speciális állapotok. Két speciális állapot van: • Kezdő (pszeudo-) állapot (Start State): minden diagramnak kell lennie pontosan egy kezdő állapotnak. • Végállapot (End State): több is lehet egy diagramon belül. Start End Az alábbi példa mutatja a CourseOffering osztály állapotátmeneti diagramját: Initialization add student Open Closed add
student cancel cancel Canceled Állapotátmenet részletek. Egy állapotátmenethez hozzá lehet rendelni különböző egyéb jellemzőt, amelyek segítségével még pontosabbá tehetjük az osztály specifikációját (és megkönnyíthetjük a későbbi implementációt). Ezen jellemzők általában az objektum privát operációiként valósulnak meg. A következő részleteket adhatjuk meg az állapotátmenetekhez: • Akció: a viselkedést írja le, amely állapotváltáskor jellemző. • Védő feltétel: ez egy logikai kifejezés, amely az állapotátmenetet csak igaz érték esetén engedélyezi. • Esemény kiváltás: az esemény kiváltás egy üzenet, amit egy másik objektumnak küld az objektum az állapotátmenet létrejöttekor. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 58 2.5 A „kurzusfelvétel” példaprojekt Az alábbi ábrán láthatjuk a fenti állapotátmeneti diagram egy bővített
változatát az állapotátmenet részletek megjelenítésével: Initialization add student / set count = 0 ^CourseRoster.create Open [ count = 10 ] Closed add student[ count < 10 ] cancel cancel Canceled ^CourseRoster.delete Állapot részletek. Magukhoz az állapotokhoz is hozzárendelhetünk további részleteket (gyakorlatilag, itt is az objektum egyes segéd-operációiról van szó): • Belépő akció (entry): minden állapot váltáskor végrehajtódik. • Kilépő akció (exit): minden állapot elhagyáskor végrehajtódik. • Aktivitás (do): egyszer végrehajtódik valamikor, amíg az adott állapotban van. Az alábbi ábra mutatja a teljesen kidolgozott változatát a CourseOffering osztály állapotátmeneti diagramjának: Initialization do/ Initialize course offering data add student / set count = 0 ^CourseRoster.create Open entry/ Register student exit/ ^CourseRoster.Add student(Student) add student[ count < 10 ] [ count = 10 ] cancel Closed
Canceled cancel do/ Finalize course ^CourseRoster.delete 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 2.510 59 Modell vizsgálata Még ha nem is követtük szigorúan a folyamat előírásait, a tervezés ennek a pontján már szükséges egy nagyobb felülvizsgálata az elkészült modellnek és diagramoknak. A következő lépés már a rendszer fizikai megtervezése lesz, ezért fontos a logikai tervet minél pontosabban elkészíteni. A modell vizsgálatakor a legfontosabb tevékenység a homogenizálás. A homogenizálás valójában folyamatos tevékenység kell, hogy legyen, de nagyobb mérföldköveknél elengedhetetlen. Amikor homogenizáljuk a modellt, akkor a „összegyúrjuk” a projekt csapattagjainak különböző eredményeit egy egységes egésszé. Pl az elnevezéseket átvizsgáljuk, a dokumentációt kiegészítjük (át kell vizsgálni, hogy minden modellezési
anyag jól, és azonos módon legyen dokumentálva), a meglévő osztályok szerkezeteit és a kapcsolatokat felülvizsgálva átcsoportosítjuk azokat: újakat hozunk létre, a fölöslegeseket töröljük. Szükség lehet pl. arra, hogy egyes osztályok összekombináljunk, amikor azok hasonló használati esetek megvalósításai. A kombinálással kisebb, áttekinthetőbb modell készíthető és a fölösleges elaprózódás elkerülhet ő. Egy másik tipikus tevékenység az, amikor osztályokat szétválasztunk. Amikor egy osztályban megvalósított funkcionalitás túl nagy és szerteágazó, akkor célszerű szétválasztani azt külön osztályokba. Ne felejtsük el az alapelvet, miszerint „egy osztály egy dolgot csináljon, de azt jól tegye”. Bizonyos osztályokat teljes egészében is megszüntethetünk. Az osztályok eliminálása akkor szükséges, amikor nincs sem lényeges viselkedése sem strukturája, és nem vesz részt egyetlen használati esetben sem. Ez
előfordulhat bármely osztállyal a modellezés során, hiszen a modell folyamatosan változik. A leglényegesebb rész a vizsgálatkor talán az, hogy a modell önmagában konzisztens maradjon. Igaz, hogy modellezéskor minden résztvevő tudatosan erre törekszik, de előfordulhatnak tévedések. A konzisztencia-vizsgálat alapvető szempontja az, hogy minden statikus és dinamikus nézetet össze kell egyeztetni, az azonos elemek különböző helyeken azonosan jelenjenek meg. Egy jó módja a konzisztencia vizsgálatának, az, amikor a szcenáriókat végigpróbáljuk, az eseményeket végigkövetjük (ld. a RUP fő iterációinak 5 lépését!) 2.511 Rendszer-architektúra Miután a rendszerünknek elkészült egy elsődleges modellje és az már különböző diagramokként meg is jelenik, célszerű a rendszer architektúráját megtervezni. Ide tartoznak az eddig elkészült logikai modellen kívül különböző nézetek szerint a fizikai felépítés és a futás közbeni
működés magas szintű ábrázolása. A rendszerarchitektúra valójában stratégiai döntések sorozata a rendszer szerkezetével, viselkedésével, rendszerelemek együttműködésével és fizikai megvalósulásával kapcsolatban. Ez egy nagyon fontos dolog, fejleszt ői csapat kitüntetett feladatai közé kell sorolni, hiszen ez a rendszer nézeteinek a legfelsőbb szintje. Az architektúráról a 2.41 részben beszéltünk részletesebben, itt használjuk az ott megismert fogalmakat. Az architektúra 4+1 nézetét vesszük alapul, és ezen belül definiáljuk az egyes nézeteinket, diagramjainkat. A használati eset nézet. A használati eset nézet (Use Case View) az architektúrának egy áttekintő nézete, amely a rendszer használhatóságának és Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 60 2.5 A „kurzusfelvétel” példaprojekt működésének megértését segíti elő a funkcionalitás szempontjából. Az
egyéb nézeteket áttekinti és validálja. Alapvető elemei a használati eset diagramok, valamint a kölcsönhatás diagramok, amelyek korábban már részletesen is tárgyaltunk. A logikai nézet. Az architektúra logikai nézete (Logical View) alapjában véve az osztálydiagramok: osztályok, csomagok, kapcsolatok. Ebben a nézetben a rendszer logikai szerkezete van leírva. Az alábbi ábrán láthatjuk a Kurzusfelvétel alkalmazás teljes logikai szerkezetét, azaz a fő osztálydiagramot: Interfaces GUI Controls PeopleInfo UniversityArtifacts Foundations Database global Error Handling global A komponens nézet. A komponens nézet (Component View) is a rendszer szerkezetével foglalkozik, csakhogy ez a rendszert annak fizikai szerveződése által ábrázolja. Itt a különböző szoftvermodulok szervezésének ábrázolása a fő cél Főleg implementációs kérdésekkel foglalkozunk, mint pl. a programozási nyelv, felhasználói interfész, hardver platform, stb. A
logikai és a komponens nézetek között nem mindig van 1-1 megfeleltetés, de a kettő konzisztens a modellel. A komponens nézetnek az elemei a komponens diagramok, amelyeket a logikai nézethez hasonlóan csomagokba csoportosíthatjuk. A komponens diagramok főbb elemei a komponensek, amelyek különböző jellegű implementációs egységeket ábrázolnak, pl. forrásfájlok, modulok, bináris könyvtárak, stb Az egyes programozási nyelvekben történő implementációkor különböző dolgokra lehet használni a komponenseket, C++ esetében pl. egy komponens = h + cpp fájl, Java esetében viszont egy komponens ábrázolhat egy osztályt, vagy egy osztálygyűjteményt .jar formátumban Az alábbi ábrán a komponens néhány megjelenítési formáját láthatjuk: Package Specification Task Specification Component 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design 61 Az alábbi ábrán
láthatunk egy példát a Kurzusfelvétel alkalmazásból (University csomag komponens diagramja): Course RegistrationUser Course Offering Professor Student Az alábbi ábra egy másik példát mutat egy olyan komponens diagramra, amelyben a komponensek nagyobb egységek, bináris program modulok. Ezen kívül az egyes komponensek közötti kommunikáció is fel van tüntetve, az interfészekkel együtt. Az ilyen fajta függőségi viszonyok ábrázolása a folyamat nézetben is hasznos lehet (ld. lejjebb) <<EXE>> Professor Options <<DLL>> Persistence <<DLL>> Courses CoursesAPI DatabaseAPI A folyamat nézet. A folyamat nézet (Process View) a rendszer logikai architektúráját szemlélteti, de annak nem szerkezeti felépítését, hanem futás közbeni implementációs kérdésekkel foglalkozik, pl. teljesítmény, skálázhatóság, megbízhatóság. A folyamat nézetben a rendszer elemei (komponensei) közötti függőségeket
vizsgáljuk, ezért az alapvető elemei a komponens diagramok, kiegészítve különböző függőségi kapcsolatokkal. Pl a hívási függőségeket ábrázolhatjuk, amint a a futtatható főprogram .exe hívogatja a DLL-ben megvalósított egyéb komponenseket (ld. fenti ábra) A telepítési nézet. A telepítési nézet (Deployment View) a rendszer további fizikai felépítését, szerkezetét ábrázolja, de itt már sokkal nagyobb léptékű elemeket figyelembe véve. Igazából, valódi nagy méretű rendszerek modellezésénél alkalmas a telepítési nézet, amikor a rendszernek több különálló feldolgozási egysége van, amelyek esetleg hálózatba vannak kötve és egy osztott rendszerként működnek. A telepítési nézet az ilyen fizikai elrendezés topológiáját ábrázolja, a célja különböző szempontok könnyebb vizsgálata, pl. teljesítmény, megbízhatóság, skálázhatóság, stb. A telepítési nézet elemei a telepítési diagramok, amelyek
csomópontokból (node) állnak. Egy csomópont a rendszer egy-egy futás közbeni feldolgozó eleme, mint pl. szerver, kliens, adatbáziskezelő szofter vagy gép, stb. A csomópontok közötti összeköttetés asszociációs kapcsolatot jelent, amely az egységek között kommunikáció meglétét jelzik. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 62 2.5 A „kurzusfelvétel” példaprojekt Az alábbi ábra mutatja a Kurzusfelvétel rendszer egy lehetséges topológiáját. Láthatjuk, hogy az egyes csomópontok egy-egy számítógépen futó önálló alkalmazást jelképeznek, amelyek egy hálózat segítségével vannak összekötve és különböző futtatható programokat jelentenek, amelyeket a különböző felhasználók (oktató, hallgató, ) használhatnak: Database Server Registration ProfessorOptions.exe Library Dorm Main Building StudentOptions.exe StudentOptions.exe StudentOptions.exe 2.512 Fejlesztési
folyamat iterációi A fenti tíz lépésben áttekintettük a Kurzusfelvétel példa alkalmazás egy lehetséges modellezési lépés-sorozatát. Az egyszerűség kedvéért nem követtünk azonban pontosan egy jól megtervezett iterációs tervet, ahogy az a RUP folyamat javasolná. Kis rendszerek esetében nyilván nem lenne szükséges ragaszkodni a folyamat használatához, viszont valós méretű projektek esetében annál inkább! Mint ahogy azt már leírtuk a 2.31 részben, a folyamat szerint egy 0 kezdeti iteráció után részletes iterációs tervet kell készíteni a további iterációkról. Ez általában úgy szokott történni, hogy a használati esettanulmányban szereplő egyes szcenáriók megvalósítást helyezzük sorrendbe. Olyan nagyobb egységekre bontjuk így szét a rendszert, amelyek jól különválasztható főbb funkcionalitásokat képeznek. Az egyes szcenáriók sorrendbe helyezésekor az alapvető prioritás a rizikó legyen, tehát lehetőleg a
legrizikósabb részeket valósítsuk meg előbb. Ez annyit jelent, hogy pl. kevésbé baj a megrendelő számára, ha egy rész-, vagy kiegészítő funkcionalitás nem készül el időre, ha a fő funkcionalitások már jól működnek. Itt nyilván azt is figyelembe kell venni, hogy az egyes iterációkban megvalósított használati esetek és egyéb elemek mennyire építenek egymásra, tehát előbb a függő elemeket kell megvalósítani. A Kurzusfelvétel alkalmazás modellezéséhez használhatjuk pl. a következő iterációs tervet: • 1. iteráció Oktató adatainak kezeléséhez szükséges használati esetek és modellek, valamint a „Kurzus meghirdetése” használati eset megvalósítása. Ez a legfontosabb iteráció, hiszen itt a fő adatbázis megvalósításának rizikóját kell megszüntetni. Továbbá, e iteráció elemeire épít a másik két iteráció is • 2. iteráció Hallgató adatainak kezeléséhez szükséges használati esetek és modellek,
valamint a kurzuskatalógushoz szükséges dolgok megvalósítása. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 2 Objektumorientált analízis és design • 63 3. iteráció „Kurzusfelvétel” és a kurzusválasztások listái használati eseteinek megvalósítása. Miután a fent három iteráció mindegyikét végrehajtottuk, készen vagyunk a rendszer minden funkcionalitásának megvalósításával. Az egyes iterációk önmagukban teljes egészet kell, hogy képezzenek, tehát a tervezési feladatoktól kezdve az implementáción keresztül a tesztelést is magukba kell, hogy foglalják. Konkrétan, az alábbi főbb tevékenységeket kell elvégezni minden iterációban (ahogy azt a fejezet megelőző részében is láttuk a lépéseket): • Felhasználói felülettervezés. • Osztályok létrehozása. • Tervezési minták használata. • Kapcsolatok tervezése. • Attribútumok és operációk
hozzáadása. • Öröklődések megtervezése. • Kódolás, tesztelés, iteráció dokumentálás. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 3 Objektumkomponens technológiák 65 3 Objektumkomponens technológiák 3.1 Bevezetés Osztott számítások elvégzése objektumok segítségével egy viszonylag új keletű, de nagyon hatékony módszer számos, mai hálózatos feladat elvégzésére. Régebben, különböző számítógépek közötti programozás bonyolult volt és a hibalehetőségek nagyok voltak. A hálózatos protokoll rétegeit ismerni kellet, és különböző konvertálási feladatokat kellett elvégezni. Osztott számításkor egy hálózat komponensei egy egységként tudnak együttműködni, objektumok megosztásával egy heterogén és átlátszó hálózaton keresztül. Ilyen jellegű együttműködésre számos technológia lett már kifejlesztve, de mindegyik alapvető tulajdonsága, hogy
alkalmazásukkal maga a hálózat is egy gigantikus számítógépként üzemel. Az osztott objektumkomponens technológiák a hagyományos objektumorientált programozásra és paradigmára épülnek. Az alapvető különbség az, hogy az objektumok nem csak magában a hagyományos értelemben vett alkalmazásban léteznek, hanem megosztva, a hálózat tetszőleges pontján létezhetnek, ahol gyakorlatilag akármilyen operációs rendszer, környezet, stb. üzemel Mindez viszont úgy néz ki az alkalmazás számára, mintha az objektumok lokálisan léteznének. Az osztott objektumkomponens technológiák alapvető célja tehát az, hogy a megosztott információs technológiákat (pl. hálózatos kliens-szerver alkalmazás együttműködések) hatékonyabbá, flexibilisebbé és kevésbe bonyolulttá tegyék. Néhány alapvető előnye ezen technológiáknak a következő: • régi, elavult szoftverrendszereket technológiák alkalmazásával, • egy alkalmazást úgy is ki
lehet fejleszteni, hogy egyes komponenseit osztottan valósítsuk meg, mindig a legmegfelelőbb platformot választva nekik, • a kliens számára minden objektum lokálisnak tűnik, így azzal sem kell törődnie, hogy azok eredetileg milyen platformon léteznek, • a rendszerek integrációját sokkal sikeresebben lehet elvégezni, stb. könnyebb újrafelhasználni modern Az ilyen jellegű szoftverfejlesztéssel újabb szempontokat is létrejönnek a fejlesztéskor használható eszközök terén. Egyre több szoftver cég a termékeit komponensek formájában kínálják, amelyek valamilyen objektumkomponens technológiának felelnek meg, így a fejlesztő munkája sokkal hatékonyabb és megbízhatóbbá válik. A következőkben a legismertebb technológiákat ismertetjük röviden. COM. A Microsoft Windows Distributed Internet Applications (Windows DNA) architektúrájának komponens technológiája (Component Object Model). Hálózat alapú alkalmazásoknál a
kliens/szerver elvet követve, egységes architektúrát és nyelv-független, átlátszó megoldást nyújt. A COM+ ennek továbbfejlesztése, a DCOM (Distributed COM) már az ORPC (Object Remote Procedure Call) protokollon alapuló osztott technológia. A DCOM objektumainak egy interfésznek Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 66 3.2 CORBA kell megfelelniük, az alacsony szintű memóriaelrendezést már a protokoll kezeli. Alapvetően bináris szinten vannak ábrázolva az objektumok, így tetszőleges nyelven lehet megvalósítani a klienst és a szervert. Alapvetően Windows platformos megoldás, de létezik UNIX, Linux és Solaris megvalósítás is. JavaBeans, Enterprise JavaBeans. Ez egy platform-független komponens architektúra Java környezetben. Nagyon nagy mennyiségű újrafelhasználható program elemet tartalmaz, amellyel hatékony alkalmazások készíthet ők. Minden bean-nek néhány alapvető
követelményeknek eleget kell tennie, és innentől kezdve tetszőleges funkcionalitás megvalósítására alkalmas. Erről bővebben a 4 fejezetben lesz szó. Java RMI. A JRPM (Java Remote Method Protocol) segítségével megvalósított távoli metódus hívás (Remote Method Invocation). Objektumok szerializálásával valósul meg a Java objektumok átvitele a hálózaton keresztül. Megfelelő interfész használatával Java programok között távoli kommunikáció könnyen megvalósítható. Erről is később bővebben lesz szó CORBA. A CORBA (Common Object Request Broker Architecture) az egyik legismertebb, leghatékonyabb és legáltalánosabban használható technológia, ezért ezzel részletesebben foglalkozunk. A CORBA segítségével programozási nyelvtől és platformtól függetlenül újrafelhasználhatók az objektumok, a Java pl. közvetlenül támogatja. 3.11 Hivatkozások CORBA Basics. • Object Management Group, 2002 The Common Object Request Broker:
Architecture and Specification, Version 2,6 • Object Management Group, 2001 Gopalan Suresh Raj: A Detailed Comparison of CORBA, DCOM and Java/RMI • http://gsraj.tripodcom/misc/comparehtml Bruce Eckel: Thinking in Java • Prentice Hall, 2000 • http://www.mindviewnet/Books/TIJ Web címek: • http://www.omgcom/ • http://www.corbaorg/ 3.2 CORBA 3.21 Bevezetés A CORBA (Common Object Request Broker Architecture) az OMG szervezet (Object Management Group) által 1990-ben létrehozott és karbantartott architektúra és infrastruktúra. A CORBA lehetővé teszi, hogy különböző nyelvekben megírt és különböző platformokon futó alkalmazások 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 3 Objektumkomponens technológiák 67 együttműködjenek betartva az objektumorientáltság elvét, azaz objektumok segítségével. Ezen alkalmazások futhatnak ugyanazon a gépen is, de alapvetően tetszőleges helyen is
elhelyezkedhetnek tetszőleges hálózatokon belül. Egyik alapvető alkalmazása a CORBA technológiának a hálózatos szerverek megvalósításánál van, amelyeknek nagy mennyiségű klienst kell kiszolgálniuk megbízhatóan és hatékonyan. A világ legnagyobb web kiszolgálói között számos olyan van, amely CORBA alapú (a CORBA internetes honlapján néhány sikertörténet is olvasható). A CORBA sikeresen ötvözi az objektumorientált és az osztott kliens-szerver paradigmák elveit. 3.22 A CORBA alapvető működési mechanizmusa A CORBA-t támogató alkalmazások objektumok segítségével kommunikálnak. Az objektumok a CORBA nyelvezet szerint egy identitással, interfészszel és implementációval rendelkező entitás. Ezen objektumoknak egységes interfésznek kell, hogy megfeleljenek, amelyet a szerver és a kliens is ismer. Az, hogy az interfésznek megfelelő objektumnak milyen konkrét implementációja van, a CORBA protokolljai elrejtik. A kommunikáció
tehát kliens/szerver alapú, ahol a kliens a szervertől kér egy objektumot. A kért objektum beazonosításáért, annak továbbításáért az ún. Object Request Broker (ORB) kommunikációs infrastruktúra felelős, amelynek a kliensnél és a szerveren is egyaránt futnia kell. A lényeg, hogy az ORB-k elrejtik a kliens implementációja elől, hogy a szerveren az adott objektum valójában hogyan is van megvalósítva, milyen nyelven, platformon, stb. Maga az alacsony szintű kommunikáció egy saját protokoll szerint történik, az IPre alapozva, melynek neve Internet Inter-ORB Protocol (IIOP). Az ORB feladatai közé tartozik, hogy létrehozzák a megfelelő kapcsolatot a kliens és a szerver között, valamint az, hogy a kért objektumhoz tartozó métódushívásokat megfelelő argumentumokkal együtt bináris folyamban átküldjék. Ehhez nyilván sok részletet kell megvalósítania az ORB-nek, mint pl a szerver beazonosítása, szerveren futó alkalmazás elérése és
az objektum elérése. Egy objektumot a kliens egy hivatkozással tud elérni (reference), amely hivatkozás az ORB számára egy egyedi azonosítót jelent, de a felhasználó névvel is elláthatja ezeket az objektumokat. A nevek kezeléséért egy különálló CORBA szolgáltatás felelős, a naming service. Az ORB egy magasabb szintű architektúrának része, amely az egész technológiát specifikálja. Ez az ún Object Management Architecture (OMA) Az OMA a következő főbb elemeket tartalmazza: • A CORBA rendszerrel kapcsolatos elemek: az ORB-k és a szolgáltatások (Object Services). • Alkalmazás orientált elemek: Common Facilities szolgáltatások, pl. nyomtatás), Application Objects Objektumorientált nyelvek és módszertanok (magas szintű 2002, Beszédes Árpád és Ferenc Rudolf 68 3.2 CORBA APPLICATION OBJECTS COMMON FACILITIES OBJECT REQUEST BROKER OBJECT SERVICES Ezek közül az Object Services olyan fontos szolgáltatásokat nyújt,
mint például a Naming Service, amely a kliens számára az objektumok beazonosítását segítik azok tulajdonságaik, nevük alapján. Az Application Objects maguk az alkalmazások, amelyek a többi komponenst használják. Távoli hívás ORB-vel. Az ORB architektúrának egyik alapelve az, hogy az objektum interfészét egyértelműen szét kell választani az implementációtól (hiszen az nem is látszik a kliens számára). Az interfész egy, arra szolgáló programozási nyelven, az IDL (Interface Description Language) segítségével leírja, hogy az objektumnak milyen operációit lehet meghívni. Az objektum implementációját servant-nek (szolga) hívják, amely az implementáció programozási nyelvén (pl. Java, C++ vagy egyéb) megvalósítja az interfész operációit. Amikor megadjuk egy objektum interfészét az IDL segítségével, abból keletkezik két kódrészlet, az ún. stub és a skeleton, amelyek az ORB-nek egyfajta kommunikációs elérési pontját
jelentik az adott objektumnak. A stub a kliens részén van, a skeleton meg a servant-nak a része. Egy távoli hívás kezdeményezésének elején a kliensnek először is a kért objektumra egy referenciát kell kérnie (ez többféleképpen is történhet, a Naming Service az egyik). Az ORB ezen hivatkozás szerint a hívást a távoli objektum felé továbbítja a stub-ok és a skeleton-ok segítségével (ezt a kliens nem érzékeli). Alul láthatjuk a CORBA ezt a rendkívül leegyszerűsített működését: in args CLIENT obj ref operation() out args + ret IDL stub ORB1 OBJECT (servant) IDL skel IIOP ORB2 IDL. A nyelvi függetlenséget az IDL használata teszi lehetővé: a kliensnek mindössze a metódusok neveit és szignatúráit kell ismernie, amelyeket a szerver szolgáltat. A szerver ezután a metódushívást meg tudja valósítani az implementációs nyelvben. Az IDL nyelv segítségével definiáljuk az objektumunk interfészét, majd ezután ezt az interfészt
egy IDL fordító segítségével le kell fordítani és így kapjuk meg a stub és a skeleton kódokat. Az adott CORBA implementációnak a lehetőségeit felhasználva ezután a megfelelő szolgáltatások elérésével megvalósíthatjuk a kommunikációt. Maga az IDL nyelv szintaxisa egyébként nagyon hasonlít a C++ és Java szintaxisához: vannak csomagok 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 3 Objektumkomponens technológiák 69 (modulok), interfészek, metódusok, típusok, stb. A 4153 részben bemutatjuk az IDL használatát egy példán keresztül és egy alapvető CORBA kapcsolat létrehozását Java-ban. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 4 Objektumorientált nyelvek 71 4 Objektumorientált nyelvek 4.1 Bevezetés A programozás alapvetően a komplexitás kezeléséről szól. Különböző nyelvek különbözőképpen kezelik a komplexitást.
Mitől növekszik a komplexitás? A követelmények bővülésével a nyelvek is bonyolultabbak lesznek. Ezen kívül felléphetnek kompatibilitási problémák is. Pl: C++ kompatíbilis C-vel, Perl Seddel, Visual Basic BASIC-el, stb A Java nyelv készítőinek alap célkitűzése volt a komplexitást csökkenteni a programozó szempontjából. A Java egy relatíve új nyelv, de sok jó tulajdonságot átvett régebbi nyelvekből, pl. a C, C++, Smalltalk, stb-ből A komplexitás kezeléséből adódik, hogy: • könnyebb robusztus kódot írni (pl. ekvivalens programot C++ -ban kétszer annyi ideig tart megírni), de • lassabban fut. Hátrányai viszont megtérülnek, mert gyorsabban lehet jó Java programozóvá válni, és könnyebb megbízható programot írni. Ezen kívül Web alkalmazásoknál kiváló kommunikációs eszköz. A Java egy magas szintű programozási nyelv, ahol nem a kódoláson van a hangsúly, hanem a tervezésen. Jelentős szerepe volt/van a Web
elterjedésében, mert kifinomult osztálykönyvtárakat nyújt az internetes programozás elősegítésére, és beépített biztonsági rendszere van. Ezen kívül fontos még, hogy platform-független Ezáltal megszűnnek a portolási problémák a különböző hardver platformokhoz. A Java nem „csak” egy nyelv. Számos egyéb alkalmazhatósága van (applet, j2me, beágyazott rendszerek), jó osztálykönyvtárakat és programozási interfészeket nyújt (többek között tervezési mintákat is használ). 4.11 Előismeretek A jegyzet épít bizonyos alapismeretekre. Mindenképpen szükségesek az algoritmikus programozás alapjai és az objektumorientált programozás alapjai. Bizonyos helyeken hasznos lehet a C, de még inkább C++ nyelvek ismerete. 4.12 Hivatkozások Bruce Eckel: Thinking in Java: • http://www.mindviewnet/Books/TIJ A Sun honlapja: • http://www.suncom Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf
72 4.2 4.2 Absztrakció Absztrakció Minél bonyolultabb a programozás problémája, annál absztraktabb megoldások kellenek. Ha visszatekintünk a múltba, akkor láthatjuk, hogy megalkották az assembly-t, ami absztrakció a gép felett. Majd következett a Fortran és C, amik absztrakciók az assembly felett. De ezek még mindig gép-orientált elvonatkoztatások. Ennek felismerése után következtek a probléma-orientált absztrakciók, mint pl. a LISP és APL Ezeknek azonban korlátozott volt az alkalmazhatóságuk. A mai legmodernebb megoldás az OOP, vagyis az Objektumorientált programozás. Absztrakció OOP-ben: 4.3 • Alkalmazhatóság-független absztrakciója a probléma elemeinek. • Ezeket hívjuk objektumoknak. OOP jellemzői Egy OO programban minden objektum. Ezek után egy OO Program valójában nem más, mint egymással kommunikáló objektumok összessége. Minden objektumot kisebb objektumokból állítunk össze (akár alaptípusúakból). Minden
objektumnak van típusa, ami egy osztály (Java-ban a class kulcsszóval definiáljuk). Ugyanolyan típusú objektumok általában ugyanolyan üzeneteket fogadhatnak (a gyakorlatban ez relaxálható: ld. polimorfizmus) 4.31 Objektum interfésze A sok egyedi objektum között vannak olyanok, melyeknek közös tulajdonságaik és viselkedéseik vannak, vagyis egyazon családba – osztályba tartoznak. A Simula-67 volt az első ilyen nyelv. Az osztály valójában nem más, mint egy absztrakt adattípus (egységbezárás – encapsulation). Ugyanúgy viselkedik, mint minden egyéb primitív típus (pl. változó hozható létre) Az osztályoknak van tulajdonságuk (állapot) és viselkedésük. Egy osztály tulajdonságát az attribútumai (adattagok, mezők) írják le, míg a viselkedést az operációk (metódusok, tagfüggvények). Ezek a viselkedések üzeneteken keresztül aktiválhatók. Minden operációnak (és osztálynak) kell, hogy legyen interfésze, ami leírja, hogy mi
az, amit üzenhetünk (deklarálás), és implementációja, ami teljesíti a kérést (definíció). Az osztály egy példányát nevezzük objektumnak (mi a konkrét állapot). Pl. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 73 Light lt : Light brightness brightness=100 on() off() brighten() dim() Objektum Probléma eleme a valóságban Típus név brightness = 100; Interfész UML osztálydiagram Light lt = new Light(); Java kód lt.on(); 4.32 Implementáció elrejtése és újrafelhasználása Programozás közben két feladatot szoktunk megoldani: Osztályt gyártunk (magunknak vagy másoknak) illetve osztályt használunk (mienket vagy másét). Fontos, hogy ha osztályt gyártunk, akkor elrejtsük az implementációját. A használója nem kell, hogy ismerje, nem tudhat róla, így nem is teheti tönkre. Ezáltal kevesebb lesz a programhiba. Az implementáció elrejtését az elérés
vezérlésével (láthatóság) valósíthatjuk meg. Ezáltal lehetőség nyílik az implementáció biztonságos módosítására akár későbbi fázisokban is, amikor már sok helyen használva van az osztály. A Java-ban használható elérés vezérlők (access specifiers) a következők: • public (publikus), private (privát), protected (védett) és az alapértelmezett: „friendly” (barátságos – csomagon belül publikus, egyébként privát) Az újrafelhasználhatóság az OOP egyik legfontosabb előnye. Az objektumokat általában más objektumokból rakjuk össze. Ezt nevezzük kompozíciónak és aggregációnak. A tartalmazott osztály általában privát elérésű : Car : Engine Kompozíció UML objektumdiagrammal bemutatva Car Engine Kompozíció UML osztálydiagrammal bemutatva 4.33 Öröklődés Egy osztály interfészét újra fel lehet használni, hogy a „hasonló” osztályokat ne kelljen mindig újra létrehozni. Inkább „klónozni”, majd
bővíteni, esetleg módosítani. Ezt öröklődésnek nevezzük. Amelyik osztály interfészét újrahasznosítjuk az ős (szülő, alap), amelyik osztály felhasználja az interfészt a gyerek (leszármaztatott). Ha az ős változik, a származtatott is módosul. Az elérhet őség fontos: a privát Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 74 OOP jellemzői 4.3 attribútumok és operációk nem elérhetők (de továbbra is benne van), a védettek igen. A hasonlóság kifejezését az ős felé általánosításnak nevezzük, a különbséget a gyerek felé pedig specializálásnak. A származtatott új típus lesz, ami az ős interfészét duplikálja – azonos típusú az őssel! (A kör az egy alakzat.) Shape draw() getColor() Circle draw() Square Triangle draw() draw() Származtatás UML osztálydiagrammal bemutatva Származtatással ún. osztályhierarchiák hozhatók létre A származtatott osztály bővíthetjük
úgy, hogy új attribútumokat veszünk fel, illetve új operációkkal. Ezt két féleképpen tehetjük meg: 4.34 • teljesen új operációkat veszünk fel, illetve • módosítjuk az ős viselkedését (interfész marad): felüldefiniálás (overriding) Objektumok felcserélhetősége: Polimorfizmus Megtehetjük, hogy egy objektumot az őstípusa alapján kezeljük (pl. Shape) Ez előnyös lehet, mert a kód, ahol használjuk az objektumot, nem függ a specifikus típusoktól; és utólag is lehet definiálni származtatottakat. Ilyenkor az ős osztály operációit hívjuk. A fordító ebben az esetben nem tudja, hogy melyik konkrét implementáció hívódik, mert a programozó nem is kívánja megmondani. Futás közben fog kiderülni a konkrét típus alapján. Nem OOP nyelvekre a korai kötés a jellemző, amikor a hívott eljárás abszolút címe fordítási időben meg van adva. OOP-re a kései kötés a jellemző, amikor fordítási időben csak a kandidátusok
adottak. Ez lassabb, de sokkal flexibilisebb kódot eredményez Java-ban minden operációhívás ilyen virtuális típusú (C++-ban külön meg kell mondani). A polimorfizmus segítségével könnyen bővíthető a program, mert nincs típus specifikusság. Pl Shape helyett Circle van átadva (megtehető, mert a kör az egy alakzat is) Ezt nevezzük upcasting-nak („beleolvasztás”). void doStuff(Shape s) { // . s.draw(); } Circle c = new Circle(); Triangle t = new Triangle(); doStuff(c); doStuff(t); 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 75 A doStuff operációban az s.draw() hívás nincs megkülönböztetve a típusok szerint. Melyik draw() fog hívódni? Fordítási időben nincs megkötve Egy speciális mechanizmus működik, amely futás közben rendeli hozzá a megfelelő implementációt (kései kötés). 4.35 Absztrakt- és interfész osztályok Egy (ős)osztály absztrakt, ha csak
interfészként van használva (nem jön belőle létre objektum). Java-ban az abstract kulcsszóval adhatjuk meg Absztrakt osztályokban lehetnek absztrakt operációk is, ezeknek nincs törzsük (definíciójuk). Egy absztrakt osztályból származtatott gyerek osztály is absztrakt lesz, ha nem implementálja az operációt. Létezik egy speciális típusú absztrakt osztály, az interfész osztály, amikor operáció definícióból egy sem lehet (csak az interfész van deklarálva). Nagyon hasznos, mert különválasztja az interfészt az implementációtól. Java-ban az interface kulcsszóval adhatjuk meg. Egy osztály több interfész osztályból is öröklődhet (több egyszerű osztályból nem). 4.4 Java bevezetés 4.41 Egyszeres gyökerű hierarchia A legtöbb OOP nyelvben mindig van egy beépített ős: Java-ban: Object, ami minden osztálynak az őse (még a sajátjainknak is). Előnyös lehet, mert nem kell mindig hierarchiát építeni az alaptól, és bizonyos
alap-funkcionalitások eleve elérhetőek lesznek. Pl az argumentum-átadás könnyebb (upcasting), és a szemétgyűjtő (lásd később) működése is egyszerűbb. Azonban ez egy kis megkötést is jelenthet (pl. C++-ban nincs beépített ős) 4.42 Objektumok élete Az eddigiek szerint az OOP = egységbezárás + öröklődés + polimorfizmus. Vannak azonban egyéb fontos tényezők is, mint pl. az objektumok tárolási helye: • stack: automatikus és gyors, de nem mindig megfelelő. • static: statikus, nem flexibilis de gyors. • heap: dinamikus, futás-közbeni, lassúbb. A másik fontos tényező az objektumok felszabadítása: • stack: automatikus • heap: kézzel kell, ami hibalehetőségeket rejt magában Java-ban az objektumok mindig a heap-en keletkeznek (kivéve a primitív típusokat). Létre kell őket hozni a new kulcsszóval A felszabadítás pedig automatikus ún. szemétgyűjtő (garbage collector) segítségével Ezáltal biztonságos, könnyebb a
kezelés, de sokkal lassúbb (mindig heap, mindig polimorfizmus), mert több helyről is hivatkozhatnak egy objektumra (pl. konténer tárolja, de máshol is használjuk). Ez gond lehet real-time programoknál Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 76 4.4 Java bevezetés C++-ban a szemétgyűjtő hiánya miatt ez súlyos probléma-forrás lehet, de megmaradt a C hatékonysága. 4.43 Kollekciók Nem lehet mindig előre tudni, hogy hány objektum kell, mert változó élettartamúak lehetnek. Ilyen célokra fejlesztették ki a kollekciókat (konténerek), amik valójában tároló objektumok, hivatkozásokat tárolnak a tárolandó objektumokra. Egyszerűbb feladatokra például alkalmas lehet egy egyszerű tömb is, de ennél sokkal jobbak és rugalmasabbak a beépített kollekciók, mert automatikusan karban tartják magukat (átméretezés, stb.) C++-ban az STL szabványos osztálykönyvtár szolgálja ezt a célt, Object
Pascalban a VCL, míg Java-ban a standard könyvtár része Különböző célokra különböző konténerek ajánlottak, mert különböző műveletigényűek bizonyos műveleteknél. Pl a vektor gyors elérést, de lassú beszúrást valósít meg, addig ez a láncolt listánál fordítva van. Középút lehet a halmaz vagy hash tábla, amiknek viszont a tárigényük nagyobb. Egy konténer által tárolt elemek elérése iterátorral van megvalósítva. Ugyanúgy használható minden típusú konténerrel (ArrayList, LinkedList, Stack, Set). Ez előnyös, mert bármikor megváltoztatható az alkalmazott konténer, anélkül hogy a programunk módosulna! Konténerre és iterátorra sokszor szükség van. Nem kell mindig újra megírni, része a könyvtárnak, nem kell vele foglalkozni, a valós problémára lehet koncentrálni Java-ban minden konténer Object-eket tárol. Beszúráskor ez nem jelent gondot, mert upcasting történik (ez OK). Eléréskor viszont downcasting kell, ami
nem mindig biztonságos (nem tudhatja, hogy milyen objektum is igazából). Ilyenkor meg kell jegyezni a típust valahogy, pl. egy attribútumban (van futás közbeni típus-azonosítás is, de ez lassú). Ha nem jó a downcast, akkor futási hiba történik! A megoldás a paraméterezett típusok használata (generikus programozás), ami C++-ban a template-tel van megvalósítva. Java-ban ez egyelőre csak javaslat 4.44 Kivételkezelés (Exception handling) Mindig is nagy gond volt a hibák helyes lekezelése. Sok nyelv nem is foglalkozik a témával, hanem inkább a library-ra bízza, hogy az megbízható legyen. Ilyenkor bizonyos játékszabályok betartása az egyetlen mód. A kivételkezelés viszont a nyelv (és esetleg az operációs rendszer) része! A kivétel (exception) a Java-ban egy objektum, amit a hiba helyén „dobnak” (throw) és aki képes kezelni az „elkapja” (catch). Gyakorlatilag egy párhuzamos végrehajtási ág rendkívüli esetekre, ezáltal tisztább
marad a rendes kód, és nem lehet ignorálni (míg a hibakóddal visszatérő függvényt igen). Segítségével megbízhatóan helyreállítható a program futása. A Java-ban kezdett ől fogva erősen be van drótozva a kivételkezelés. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 4.45 77 Többszálú futtatás (Multithreading) Régi követelmény, hogy egynél több feladatot egyidejűleg el lehessen végezni (multitasking). Alacsony szinten is megvalósítható megszakításokkal, ez nehéz és nem hordozható. Időzítéseknél is fontos, de még fontosabb lehet egy nagy rendszer egyes részeit külön futtatni, több szálon (thread). Interaktívabb lehet például a felhasználói felület: egy gomb megnyomható, miközben valamilyen feldolgozás a háttérben fut. Ez általában csak logikai felosztást jelent, a processzor-időn továbbra is osztozniuk kell. Ha azonban több processzor van,
akkor kihasználhatjuk a valódi párhuzamosságot úgy, hogy a programon nem is kell módosítani. Probléma lehet ilyenkor a megosztott erőforrások (pl. nyomtató, memória) közös használata. A Java nyelvbe be van építve a többszálúság támogatása Javaban minden szál egy objektum, ami képes zárolni az erőforrást (lock) a synchronized kulcsszóval (csak a memóriát tudja lock-olni). Ilyenkor más szál nem férhet hozzá, amíg az egyik zárva tartja. Egyéb erőforrásokat kézzel kell lock-olni: speciális objektumokkal. 4.5 Java és az Internet A Java több egy újabb nyelvnél, mert a World Wide Web programozására (is) ad megoldást. A Web elemei programozási szempontból (szerver/kliens elv): • Szerver (server): ahol az adat (és program) van • Kliens (client): aki eléri, feldolgozza (vagy csak megjeleníti) A Java globálisan oldja meg a probléma minden részét egységesen! 4.51 Kliens/szerver programozás Kezdetben minden interaktivitás a
szerver oldalán volt: pl. statikus HTML, CGI – Common Gateway Interface (bonyolult, lassú). A kliens programozása a megoldás: Amit csak lehet, ő hajtson végre. Ez hasonló a hagyományos programozáshoz, csak egy másik platformra: A Web böngészőt egy korlátozott operációs rendszernek lehet tekinteni. Megoldások: • Plug-in használata (böngésző bővítése) • Szkript nyelvek: HTML-ben van benne a forráskód (nem lehet titkos!). Speciális problémákra speciális nyelveket fejlesztettek ki. Ilyen például a JavaScript (semmi köze a Java-hoz), VBScript, Tcl/Tk, stb. A legtöbb böngésző támogatja a JavaScript-et alapban, de némelyikhez plug-in kell. A szkript nyelvek a problémák 80%-t megoldják, de mi van a maradék 20%-kal? Napjainkban a legnépszerűbb a Java. A kliens programozáshoz használható a Java applet, ami csak böngészőn belül fut: • Minden böngészőn működik, ahol van Java interpreter (értelmező) Objektumorientált nyelvek
és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 78 4.6 • Java alkalmazások Nem a forráskód van értelmezve, hanem a lefordított kód kerül futtatásra (általában egy JAR csomag – tömörített formátum) Egyéb, nem Java alapú megoldás az ActiveX. • Speciális komponensként tölthető le és futtatható • Nyelv-független (tetszőleges forráskódból fordítható) • Régi kód is újrafelhasználható A megfelelő technológia kiválasztásánál fontosak még a biztonsági kérdések, mert a program-letöltés vírusveszéllyel jár! Ezért korlátozzák az eléréseket (pl. Szkript, applet nem írhat lemezre). Ilyen szempontból nézve az ActiveX veszélyesebb Megoldás lehet a digitális aláírás használata, publikus kulcsú kódolással aláírt applet (signed applet). Intranet programozás (lokális, cégen belüli hálózat) esetében nagyobb biztonság érhető el, mert a hibák könnyebben felderíthetők. Ilyenkor lehet
speciálisabb a program is (pl. csak Windows alatt kell, hogy fusson) Szerver oldalon mások a feladatok: Lehet egyszerű fájl kérés teljesítése, de lehet bonyolult adatbázis lekérés + HTML formázás is. Hagyományosan Perl-lel, CGIvel valósítják meg, míg Java-ban servlet-tel, JSP-vel 4.6 Java alkalmazások A Java nem csak Internet programozásra (pl. applet) alkalmas, hanem önálló nyelvként is kiváló (tetszőleges problémára). Előnyei: • Portabilitás • Önálló alkalmazás írható (nem kell hozzá böngésző) • Gyorsan írhatók megbízható, robusztus programok Hátrányai: 4.61 • Generikus típusok nincsenek • Hatékonyság hiányzik (több oka is van) Miért ennyire sikeres? A Java készítőinek fő célkitűzése a produktivitás volt. Alapoktól indulva lett megtervezve, az eddigi programozási nyelvek tapasztalatait felhasználva. Objektum orientált, kiegészítve jó osztálykönyvtárakkal. Nyelvi szintű hibakezelést
(kivételek) építettek bele. Mindezek miatt nagy rendszerek írására is alkalmas. Egy programozó szemszögéből nézve pedig azért népszerű, mert könnyű elsajátítani egy olyan szinten, amivel már megbízható program írható. A C++ sokkal nehezebb és ezért kevesebb a jó C++ programozó. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 4.62 79 Java vagy C++? Régebben azt állították, hogy a Java leváltja majd a C++-t. Már nem így van A két nyelv más-más területeken előnyös, megfelelő célra megfelelő nyelvet kell választani. Ha Web programozás, akkor Java. Ha új nyelvet kell megtanulni, akkor Java könnyebb, de hajlamos a programozókat „lustaságra” késztetni. Az első Java-ban írt program 20-50-szer lassabb volt, mint a C++-ban megírt implementáció! Mára már sokkal jobb a helyzet, de drámai áttörés nem történt. Technikák a Java programok
gyorsítására: 4.7 • JIT – Just in time (futtatás előtti röpfordítás natív gépi kódra) • Sun hotspot • Natív fordítók Minden objektum A Java nyelv feltételezi, hogy objektum orientáltan fognak programozni benne. Valójában nem is lehet másképpen, a programozónak objektum orientáltan kell gondolkodnia. Ebben a fejezetben egy Java program alapvet ő alkotó elemeit tekintjük át, és látni fogjuk, hogy minden objektum, még maga a Java program is! Az adatok (objektumok) manipulálása két féleképpen történhet: direkt (pl. C-ben egy változó) vagy indirekt (pl. C/C++-ban pointer; C++ és Java-ban referencia) Java-ban ezt leegyszerűsítették: minden objektum, és mindig referenciával hivatkozunk rájuk (kivéve a primitív típusokat). 4.71 Primitív típusok A primitív típusok (hasonlóan a C/C++-hoz) a stack-en keletkeznek és direkt elérésűek (nem referencia által). Primitív típusok (fix bitszélesség): Prim. Típ boolean char
byte short int long float double void Méret – 16 bit 8 bit 16 bit 32 bit 64 bit 32 bit 64 bit – Min – Unicode 0 -128 -215 -231 -263 IEEE754 IEEE754 – Max – Unicode 216-1 +127 +215-1 +231-1 +263-1 IEEE754 IEEE754 – Wrapper Boolean Character Byte Short Integer Long Float Double Void A boolean értéke: true vagy false. Wrapper: objektumba csomagolja a primitív típust és a heap-en tárolja. Pl char c = x; Character C = new Character(x); További beépített típusok: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 80 4.7 Minden objektum • BigInteger: Akármekkora egész szám • BigDecimal: Akármekkora fix pontos szám Tömbök: 4.72 • Referenciákat tárol ill. primitív típusnál értékeket • Automatikusan inicializálódnak (null vagy 0) • Indexhatár ellenőrzés van futáskor (biztonságos, de lassabb) Java osztályok: Új típusok létrehozása Új típus létrehozása: egy osztályba
tartozó objektumokhoz típus hozzárendelése: class kulcsszóval, pl. class ANewType { /* az osztály törzse / } // . ANewType a = new ANewType(); Önmagában nem sok mindenre jó, csak létre lehet hozni, de nem tud fogadni semmilyen üzenetet, nem tárol még semmi hasznosat, ezért személyre kell szabni Mezők Az osztály állapotát jelzi, adatokat tárol. Részei: név, típus, kezdeti érték (nem kötelező). Egy osztály mezője (adattagja) lehet: • Primitív típusú (értéket tárol, inicializálódik) • Másik osztály típusú (inicializálás). Pl (referenciát class DataOnly { int i; float f; boolean b; } tárol), létre kell hozni new-val DataOnly d = new DataOnly(); // . d.i = 47; d.f = 11f; d.b = false; Az adatok elérése: “.” (pont) operátorral Az így kiegészített osztály önmagában még csak adattárolásra használható Metódusok Funkcionalitást biztosít az objektumoknak, meghatározza, hogy milyen üzeneteket fogadhat. Részei:
név, paraméterek, visszatérési típus, törzs Pl returnType methodName( /* paraméter lista / ) { /* metódus törzs / } Csak osztályoknak lehetnek metódusai! A metódus hívás valójában üzenet küldése az objektumnak. Pl class MethodDemo { int f(boolean b) {/*/} } MethodDemo m = new MethodDemo() // . int i = m.f(true); Osztályok használata 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 81 Egy fájlon belül: Egyszerűen használni kell, az sem baj, ha csak később lesz definiálva! Külső osztályok (pl. Java osztálykönyvtárak) esetében az import kulcsszóval be kell hoznunk egy vagy több csomagot (package-t). Pl. használni szeretnék tömböt: import java.utilArrayList; Pl. használni szeretnék mindent a util-ból: import java.util*; 4.73 Az első Java program Az első Java programunk legyen egy egyszerű kis program, ami kiírja a képernyőre, hogy „Hello” és az
aktuális időt és dátumot: import java.util*; public class HelloDate { public static void main(String[] args) { System.outprintln("Hello, it’s: "); System.outprintln(new Date()); } } A java.util–ban levő csomagok kellenek a Date osztály használatához A java.lang csomag impliciten be van impoertálva minden Java programba A java.langSystemout egy statikus PrintStream objektum Ha önálló programot írunk Java-ban, akkor • az adott fájlban kell lennie egy osztálynak, aminek ugyanaz a neve, mint a fájlnak és • ez az osztály kell, hogy tartalmazzon egy statikus main metódust a fenti alakban Fordítás és futtatás Először is, kell egy Java fejlesztő környezet (Java programming environment). Feltételezzük az ingyenes Sun JDK (java.suncom) használatát Fordítás: forrás (.java) bytecode (class) Miután begépeltük a fenti programot egy szövegszerkesztőbe és elmentettük HelloDate.java név alatt, adjuk ki a következő parancssort: javac
HelloDate.java Ennek eredményeképpen a Java fordító létrehoz egy HelloDate.class nevű fájlt. Ennek futtatásához, mivel a Java programok egy ún virtuális gépen futnak, szükség van a Java futtató környezetre (Java runtime environment). Ez része a Java fejlesztő környezetnek, de letölthető és használható önállóan is. A program futtatása a következő parancssor kiadásával kezdeményezhető: java HelloDate Ha több java forrásfájllal dolgozunk, akkor makefile-okat szokás használni. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 82 4.74 4.7 Minden objektum Megjegyzések és dokumentáció Mint általában minden programozási nyelvben, a Java-ban is használhatunk megjegyzéseket (kommenteket). Kétféle változata van: • Több sorod (C-szerű): pl. /* Ez egy Több soros megjegyzés */ • Egy soros (C++ szerű): pl. // Ez egy megjegyzés a sor végéig Egy Java forráshoz hozzátartozik annak
dokumentációja is speciális megjegyzések formájában: /* / A javadoc: program, ami összegyűjti ezeket a megjegyzéseket, segítségével készíthetünk HTML dokumentációt a programunkról. Pl. /* * Megjegyzés a soron következő osztály/metódus/mező-höz */ class A. Ebbe a speciális formátumú kommentbe HTML forrásszöveget is be lehet ágyazni, ezeket a javadoc beágyazza a dokumentációba. Ezen kívül ún tag-eket is használhatunk, amiket a javadoc értelmez, és speciálisan kezel: • @see: más osztályokra hivatkozás – linket szúr be • @version: verzió • @author: szerző • @param: metódus paraméter szerepe • @return: metódus visszatérési értékének leírása • @throws: metódus milyen kivételeket dobhat Kódolási stílus Nem hivatalos Java standard kódolási stílus ajánlott: Osztály nevek: • Nagy kezdőbetűvel kezdődnek • Ha összetett szó, akkor nincs elválasztó karakter, hanem minden szó nagybetűvel
kezdődjön • Pl. AGoodClassName Metódus-, mező nevek: • Ua., mint az osztály nevek, csak kisbetűvel kezdődnek 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek • Pl. 83 aGoodMemberName {} zárójelezés: • mint a példákban. 4.8 Programfutás vezérlés 4.81 Operátorok Értékadás = Matematikai + Összevont += %= Egyoperandusú + Relációk == <= != >= < Logikai && || ! Bitműveletek & | ^ (xor) ~ (not) Értékadással összevont &= Biteltolás << >> >>> (unsigned) Értékadással összevont <<= Háromoperandusú if-else boolean-exp ? val0 : val1 Vessző , Csak for ciklusban használható Típuskonverzió (type)value Primitív típusok között használható korlátok nélkül (kivéve a booleant). Osztályok között csak egy öröklődési fán belül engedélyezett. Objektumorientált nyelvek
és módszertanok Primitív típusoknál értékmásolást, míg objektum referenciáknál csak referenciamásolást jelent. („igazi” objektummásolás a clone() metódussal lehetséges) - * % *= /= -= / - ++ |= -> Primitív típusoknál érték összehasonlítás, míg objektum referenciáknál csak referencia összehasonlítás! („igazi” objektum összehasonlítás: equals() metódussal) C/C++-szal ellentétben csak boolean értékekre használható. Összetett kifejezés csak addig értékelődik ki, amíg ki nem derül egyértelműen az értéke ^= >>= Az eredmény int vagy long típusú >>>= 2002, Beszédes Árpád és Ferenc Rudolf 84 4.8 Programfutás vezérlés Nincs sizeof() operátor! Precedencia szabályok Operátor típusa Unáris (egyoperandusú) Aritmetikai (és eltolás) Relációs Logikai (és bitműveletek) Feltételes (háromoperandusú) Értékadás 4.82 Operátorok + - ++ -* / % + - << >>
>>> < > <= >= == != && || & | ^ ~ A > B ? X : Y = (és összetettek, pl. +=) Vezérlési szerkezetek A Java támogat minden C vezérlési szerkezetet, kivéve a goto-t. Szelekciós vezérlés: if (boolean-expression) statement else statement Visszatérés: return value; Kezdőfeltételes ismétléses vezérlés: while (boolean-expression) statement Végfeltételes ismétléses vezérlés: do statement while (boolean-expression); Számlálásos ismétléses vezérlés: for (initialization; boolean-expression; step) statement break [label]: megszakítja az aktuális (vagy a label-lel címkézett) ismétlést és a szerkezetet követő utasítással folytatja continue [label]: megszakítja az aktuális (vagy a label-el címkézett) ismétlést és a következővel folytatja Esetkiválasztásos szelekciós vezérlés: switch (integral-selector) { case integral-value1: statement; break; case integral-value2: statement; break; case integral-value3:
statement; break; // . default: statement; } 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 4.83 85 Inicializálás és takarítás Régebbi nyelvekben a nem biztonságos programozás legfőbb okai az inicializálás hiánya és az eltakarítás hiánya volt. A C++ bevezette a konstruktor fogalmát, ami garantált inicializálást jelent objektum létrejöttekor, mert egy olyan speciális metódusa az objektumnak, ami automatikusan meghívódik. Helyette lehetne például egy initialize() metódus, de ezt mindig kézzel kellene meghívni, ami hibalehetőséget jelent. A konstruktor neve mindig megegyezik az osztály nevével. A takarítás megkönnyítésére C++-ban létezik ún destruktor, ami automatikusan meghívódik az objektum megszűntetésekor. Java-ban ezt leegyszerűsítették, mert objektumot nem is lehet kézzel megszűntetni (automatikusan megszűnik, ha már senki sem használja). Ezért
nincs destruktor sem! Példa konstruktorra: class Rock { Rock() { System.outprintln("Creating Rock"); } } Mint minden más függvénynek, a konstruktornak is lehetnek paraméterei. Ezek megadják, hogyan inicializáljunk. Pl csak úgy lehet létrehozni, hogy megadjuk a magasságot is: Tree t = new Tree(12); Konstruktornak nincs visszatérési értéke, hanem objektumra való referenciát kapunk. Nem csak a korrekt inicializálás fontos, hanem az erőforrások helyes kezelése is. Java-ban ez a szemétgyűjtő (garbage collector) feladata (csak memóriával foglalkozik). Nem minden takarítást tud elvégezni Például new nélküli memóriafoglalást (pl. natív metódus által) nem tud kitakarítani Egyéb esetben nincs rá szükség, mert minden Object leszármazott el lesz takarítva. Segítség lehet a finalize() metódus, ami a végső takarítás előtt hívódik. Ezt debuggolásra is lehet használni. Fontos, hogy a szemétgyűjtés végrehajtása nem szavatolt!
Például ha még sok szabad memória van, elkerülhető a fölösleges lassítása a programnak. Kiszámíthatatlan, hogy mikor fog hívódni, tehát ez nem azonos a destruktor szerepével. Fontos, hogy kötelezően végrehajtandó feladatot ne rakjunk a finalizeba (pl fájl lezárás) Kézzel is lehet indítványozni a futtatását: System.gc(); System.runFinalization(); Működés: több algoritmus referenciaszámlálás. 4.84 szerint, de ez mind rejtve marad. Pl. Operáció-kiterjesztés (Overloading) Magasabb szintű nyelvekben neveket használunk. Természetes nyelvben is lehet több értelme a szavaknak, ilyenkor a szövegkörnyezetből derül ki az értelme. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 86 4.8 Programfutás vezérlés Programozásban ezt nevezzük overloading-nak vagy kiterjesztés-nek (túlterhelés ) (nem keverendő az overriding fogalmával, ami felüldefiniálást jelent, ld. később). ☺
Régebbi nyelvekben, például C-ben, minden név egyedi volt (nincs printf intre meg float-ra külön-külön). C++, Java-ban szükségessé vált Például, mert a konstruktornak csak egy neve lehet, mégis különböző konstruálásokat szeretnénk. A megoldás a metódusok kiterjesztése (nem csak konstruktor): • Ugyanaz a neve de más a paraméter-lista (akár üres is lehet) • Ugyanaz a feladat, miért lenne több különböző nevű függvényünk? Pl. class Tree { int height; Tree() { height = 0; } Tree(int i) { height = i; } } Az argumentum nélkül hívható konstruktort nevezzük default constructor-nak. Alapértelmezett beállításokkal rendelkező objektum létrehozására használjuk. Ha az osztályunkban nem definiálunk egy konstruktort sem, akkor a fordító automatikusan készít egy default-ot (Ha már van valamilyen – akár default akár nem – akkor nem készít). Hogyan különböztetjük meg, hogy melyik kiterjesztett metódus hívódjon? A
paraméterlistáknak egyedieknek kell lenniük. A hívás helyén az aktuális argumentumok típusai határozzák meg (ld. korai kötés) Konvertálható primitív típusoknál bonyolultabb lehet a helyzet: Ha nincs pontos egyezés, akkor az adat konvertálódik (de csak nagyobb értéktartományú típusokra). A metódus visszatérési értéke nem használható a megkülönböztetésre (lehetetlen is lenne): Pl., ha egy hívásnál a visszatérési érték nincs is használva 4.85 „this” Egy függvény kódja mindig csak egy példányban van a memóriában. Pl. Class Banana { void f(int i) {/*/} } Banana a = new Banana(), b = new Banana(); a.f(1); b.f(2); Honnan tudja az f(), hogy melyik objektumhoz lett hívva? Egy „titkos” implicit első paramétert generál a fordító. Explicite is felhasználható, pl.: • Ha a metódus formális paraméterneve megegyezik valamelyik mező nevével: this.x = x; // az els ő az attribútum • Visszaadni egy metódussal: 2002,
Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 87 class Leaf { int i = 0; Leaf increment() { i++; return this; } } /* . */ x.increment()increment(); 4.86 Mezők inicializálása Lokális változókat kötelező explicite inicializálni. Adattagok inicializálására a következő módok vannak: • Nincs explicite inicializálva – a primitívek „0” alapértelmezett (default) értéket kapnak, a nem primitívek pedig null értéket (nincs memória foglalva) • Definíció helyén • Instance initialization clause (lásd a példában) • Konstruktorral (az előző kettő után hívódik) Példa: class A { char c; // default int i = 1; // definíció helyén float f = init(); // függvénnyel B a = new B(); // nem primitív C c1; C c2; // instance initialization clause: { c1 = new C(1); c2 = new C(2); } A() { i = 2; } // default konstruktorral // előbb 1, utána 2 A(int i) { this.i = i; } } 4.87
Tömbök A tömb egy névvel ellátott (egyetlen azonosítóval kezelt) azonos típusú elemek (primitív vagy nem) sorozata. Java-ban a tömb is egy objektum Deklarálás: int[] a1; vagy int a1[]; A méretét nem lehet megadni, mert csak akkor foglalódik le, amikor inicializáljuk. Deklaráláskor csak egy referenciánk van a tömbre. Inicializálás (helyfoglalás): • Tár foglalása ekvivalens a new használatával • Inicializáló kifejezéssel: int[] a1 = { 1, 3, 4 }; • Tömbmásolással: int[] a2 = a1; Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 88 4.9 • Implementáció elrejtése Lefoglalt terület elemei 0-ra lesznek inicializálva Tömb objektumok mérete: a1.length; Csak olvasható, pl. for (int i = 0; i < a1.length; i++) System.outprintln(a1[i]); Biztonságos, mert túlindexelésnél nincs elszállás, hanem kivétel lesz dobva. Ezek az extra funkcionalitások lassíthatják a programot.
Inicializálás futás közben (méret nem ismert előre): int i; i = /* ??? /; int[] a1 = new int[i]; Nem primitív típusok tömbjei esetében csak referenciák tárolódnak. Ezért az elemeket new-val kell lefoglalni. Lehet bonyolult is az inicializálás: Integer[] a1 = new Integer[3]; a1[0] = new Integer(500); Integer[] a2 = new Integer[] { new Integer(1), new Integer(2), }; // new Integer[] elhagyható A többdimenziós tömbök hasonlóak az egydimenzióshoz int[][] a2d = { {1,2,3}, {4,5,6} }; Egyéb kombinációk: int[][][] a3d = new int[2][3][5]; 4.9 Implementáció elrejtése OO Tervezés alapkövetelménye különválasztani a változó dolgokat a nem változóktól. Osztálykönyvtárak esetén ez különösen fontos: • kliens kódja változatlan maradhat, ha a könyvtár változik is, • kívülről elérhető részek ne változzanak, de • szabadon változhat a rejtett rész Az ilyen fajta elrejtést szolgálják az elérést vezérlők (access specifiers,
láthatóság). Elérések (növekvő szigorúság szerint): • public, protected, friendly (nincs rá kulcsszó), private Könyvtár (library) egységeinek elérését is szabályozhatjuk. A Java osztályok általában ún. csomagokba (package) vannak rendezve Az elérés vezérlésének teljes értelme csak a csomagok használatával jön elő. Egy csomagot az import kulcsszóval érünk el. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 4.91 89 Csomagok Csomagokban levő osztályokra ún. minősített névvel tudunk hivatkozni Például a java.utilArrayList név a java csomagon belüli util csomagban levő ArrayList osztályt jelenti. import java.util*; Csomag importálás használata után már egyszerűen név szerint hivatkozhatunk a bennük levő osztályokra. Például elégendő ArrayList-et írni Lehet csak egy osztályra is kiadni: import java.utilArrayList; Miért van erre szükség?
Például nevek ütközésének elkerülésére. Ha nincs megadva (mint az eddigi példákban), akkor minden új osztály egy alapértelmezett csomagba default package) kerül. Ha nagy rendszert írunk, mindenképpen célszerű csomagokat használni. 4.92 Java forráskódok Egy-egy Java program írásakor fordítási egységeket készítünk (compilation-, translation unit), ezekből készülnek el a class fájlok. Minden fordítási egységben lehet egy publikus osztály (public class.) amelynek a neve meg kell, hogy egyezzen a fájl nevével (.java nélkül) Lehetnek a fájlban egyéb (rejtett) osztályok is. A java fájl fordításával class fájlok jönnek létre (egy-egy minden egyes osztályhoz). Java-ban nincs linker (a class fájlok önállóak), de lehet őket tömöríteni jar fájlba, ami lényegében megegyezik a szabványos zip tömörítéssel. A class fájlokat a Java virtuális gép értelmezi és hajtja őket végre egyenként. 4.93 Csomagok A csomag (package) nem
más, mint class fájlok halmaza. Melyik csomagba tartoznak? Forrásfájl első nem-komment sorába be kell írni: package mypackage; Ez azt jelenti, hogy a benne levő publikus osztály a megadott csomaghoz fog tartozni. Használata: mint már láttuk: mypackage.MyClass m; vagy import mypackage.*; MyClass m; Az egy csomagba tartozó osztályok egy lokális könyvtárba kerülnek a lemezen. Mitől egyediek a csomagnevek? • A teljes elérési útvonal bele van kódolva • Konvenció szerint az internet domain név is benne van fordított sorrendben (pl. comsunjava*) • Nem kötelező a használata, de javasolt. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 90 4.9 Implementáció elrejtése Hogy a Java interpreter megtalálja, be kell állítani a CLASSPATH környezeti változót, aminek tartalmaznia kell minden olyan elérési útvonalat, ahol Java csomagok elhelyezkednek el a lokális rendszeren. Jar fájlok esetében
az is hozzá van adva (nem csak a könyvtára). Előfordulhatnak névütközések, ha két import valami.* van, és mindkettőben van azonos nevű osztály, amit használni szeretnénk (amíg nem használunk ilyent addig nincs baj). Ilyenkor expliciten ki kell írni a csomag nevét az osztály elé 4.94 Elérés vezérlése Egy osztály tagjai elé oda kell írni az elérés vezérlés kulcsszavát (csak arra az egy tagra lesz érvényes). Ez lehet public (publikus), protected (védett), private (privát), vagy ha nincs kiírva, akkor alapértelmezett a “friendly” („barátságos”). A friendly (vagy “package access”) jelentése: Csomagon belül minden osztály eléri (public), csomagon kívül pedig nem elérhet ő (private). Szorosan kapcsolódó osztályokat lehet így csoportosítani (egymást használják). Ha nem friendly-t használunk, akkor minden osztálynak saját magának kell megadnia, hogy mi az, ami elérhető benne és kinek. Ami nem kell másnak az mindig
legyen privát, az interfész rész legyen publikus, a leszármazottaknak fenntartottak pedig legyenek védettek. Megjegyzés: konstruktornak is megadhatjuk az elérését. Az objektum direkt létrehozását lehet így korlátozni. Az elérés vezérlési kulcsszavak jelentése: public: Mindenki elérheti (része az osztály interfészének). Default csomagon belül (nincs semmi package megadva) mindenki használhat mindent (akár különböző fájlokban szereplők is) private: Senki nem érheti el, kivéve az adott osztály saját metódusait. Főleg rejtett implementációra és adattagokra alkalmazzák. protected: Az adott osztály és annak származtatott osztályai elérik, a többieknek nem elérhető. Pl. class Cookie { protected void bite() {} } class Chocolate extends Cookie { public void eat() { bite(); /* . */} } /* . */ Chocolate x = new Chocolate(); x.bite(); // nem elérhető x.eat(); // ok Osztály elérés vezérlése Csomagon belül az egyes osztályok elérését is
lehet szabályozni. Publikus osztály esetében a kliens elérheti az osztályt. Pl 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 91 public class Widget { . Java fájlonként csak egy publikus osztály lehet (de nem kötelező): az, amelyik a fájl nevét viseli (kis-nagy bet ű is számít). Publikuson kívül lehet még friendly, ha nincs semmi kiírva (private, protected csak ún. belső osztály – inner class lehet) Ezekből csomagon kívül nem hozható létre objektum. 4.10 Osztályok újrafelhasználása Az OOP programozás (és a Java nyelv) egyik legvonzóbb tulajdonsága a kód hatékony újrafelhasználásának lehetősége. De az újrafelhasználás nem egyszerű kódmásolást jelent. Régebbi nyelvekben próbálkozás volt erre a procedurális programozás. Ennél sokkal jobb az osztály újrafelhasználása: Ne kelljen minden osztályt újból megírni, hanem építőkockaként
felhasználni a meglévőket. A lényeg mindezt úgy tenni, hogy a meglévő kódot (osztályokat) ne módosítsuk. Kompozíció: Új osztályban meglévő osztályokból készítünk objektumot. Öröklődés: Új osztály létrehozása egy meglévő „altípusaként”, új funkcionalitás hozzáadásával. Ezek a módszerek az alap-építőelemi az OOP-nek. 4.101 Kompozíció Kompozíció: összetétel, aggregáció, rész-egész kapcsolat Egy osztály egy mezője (adattagja) másik osztály (vagy primitív) típusú (összetett osztály). Pl class A { private String s; int i; } 4.102 Öröklődés Minden OOP nyelvnek szerves része. Java-ban minden új osztály implicite örököltetve van az Object-ből (direkt, vagy indirekt módon). Új osztály származtatása meglévőből: Bővíti azt (extends kulcsszó), a származtatott az ős minden adatát és metódusát megkapja – örökli az őstől. Az öröklődés a származtatott (al-, gyermek-osztály) szempontjából
specializálás, az ős (alap-, szülő-osztály) szempontjából pedig általánosítás. Pl class Cleanser { public void scrub() {} public void apply() {} } public class Detergent extends Cleanser { // metódus módosítása (felüldefiniálás): public void scrub() { super.scrub(); /* . */ } // új metódus hozzáadása: public void foam() {} } Objektumorientált nyelvek és módszertanok /* . */ Detergent d; d.apply(); d.scrub(); d.foam(); 2002, Beszédes Árpád és Ferenc Rudolf 92 4.10 Osztályok újrafelhasználása Az ősnek van néhány interfész metódusa. A származtatott ezeket mind tartalmazza, tehát az interfész újra fel van használva. A scrub() felüldefiniálja (overriding) az ősét (specializálja a viselkedését). Az őst is hívhatja a super kulcsszó segítségével (egyébként rekurzív lenne). A foam() új metódus, bővítése az ősnek. Ős osztály inicializálása Az öröklődés nem csak az osztály interfészét másolja, hanem a
származtatottból létrejövő objektum rész-objektuma lesz az ősnek. Ős objektum ugyanolyan marad, de körül van véve a származtatott új elemeivel: ős származtatott Az őst is inicializálni kell. A származtatott konstruktorában hívni kell az ős konstruktorát (hiszen ő tudja, hogyan kell). Java-ban a default ős-konstruktor automatikusan meghívódik (mindig a legősibb előbb és így lefele). Ha nem default az ős konstruktor, hanem vannak argumentumai, akkor kézzel kell meghívni a super segítségével: class Game { Game(int i) { /* . */ } } class BoardGame extends Game { BoardGame(int i) { super(i); /* . */ } } public class Chess extends BoardGame { Chess() { super(64); /* . */ } } 4.103 Kompozíció és öröklődés Sűrűn használatos a kettő együtt. Ős osztály inicializálására a fordító kényszerít, de az adattag-objektumokra nekünk kell figyelni! Kombinált használat esetén „rendes” takarítás szükséges. Java-ban nincs destruktor,
ehelyett van a garbage collector (de ez kiszámíthatatlan). Kompozíció vagy öröklődés? Mindkettőnél az új osztály rész-objektumokat tárol. Mi a különbség és mikor melyiket használjuk? Kompozíció: egy meglévő osztály funkcionalitását felhasználjuk, de az interfészét nem (a kliens csak az új osztály interfészét látja). Ezért a beágyazott objektum általában privát típusú, de lehet publikus is (biztonságos, mert úgyis csak az interfésze elérhető) ha a kliensnek fontos tudnia hogy miből állt össze az objektum. Öröklődés: egy meglévő osztály speciális változatát készítjük el (specializálunk). Pl. melyik mi: Autó + Kerekek vagy Jármű + Autó Sokszor hajlamosak vagyunk arra, hogy mindenhol az öröklődést használjuk, mert az OOP tulajdonság! Nem mindig ez a jó. Kezdetben induljunk ki mindig a kompozícióból, majd ha kiderül hogy az új osztály mégis egy speciális típusa a 2002, Beszédes Árpád és Ferenc Rudolf
Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 93 másiknak, akkor származtassunk. Származtatásnak elsődlegesen akkor van létjogosultsága, ha ősre való konvertálás is szükséges lesz (lásd később). Túl sok származtatás és mély osztályhierarchiák nehezen karbantartható kódot eredményezhetnek! Ősre konvertálás (Upcasting) Mivel a származtatott az ős osztály egy új típusa, logikus hogy, mindenhol ahol az ős használható, ott a származtatott is használható. Ez azt jelenti, hogy ahol ős típusú objektumot vár a program, ott a származtatott egy implicit konverzión megy át az ősbe. Ezt már korábban is láttuk Példa: class Instrument { public void play () {} } class Tuner { static void tune(Instrument i) { /* . */ i.play(); } } class Piano extends Instrument { public void play() {} } /* . */ Piano p = new Piano(); Tuner.tune(p); // upcast 4.104 A final kulcsszó Több jelentése van, de a lényeg: az a mi el
van látva vele, az nem változhat (végső). Miért akarjuk korlátozni? Tervezési megfontolásból ill hatékonyság miatt. Három helyen használható: adatnál, metódusnál és osztálynál. Végső adatok Konstansadat létrehozása hasznos lehet: • Ha már fordításkor eldönthető, akkor hatékonyabb kód fordítható (konstans propagálás) • Futás közben inicializálva, de onnantól kezdve konstans. Konstans primitív adat létrehozására egyszerűen ki kell írni a definíció elé (lehet static is, ebben az esetben a címe is fix). Nem primitív típusra használva nem az objektum lesz konstans, hanem a referencia: Inicializálás után másra már nem mutathat, de maga az objektum megváltozhat. Java-ban az objektumok nem tehetők konstanssá, csak a viselkedésükkel lehet azt szimulálni. Üres végsők (blank final) olyanok, amik nem kapnak azonnal értéket, de a konstruktorban inicializálni kell. Metódus paramétere is lehet final (csak olvasható). Pl.
Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 94 4.11 Polimorfizmus class FinalData { final int i = 9; static final int VAL = 99; // csak egy van belőle final Value v2 = new Value(); final int j = (int)(Math.random()*20); // futás közben! final int k; // blank FinalData(int i) { k = i; } void f(final int i) { i++; } // hiba: i nem változhat } Végső metódusok Két dologra használjuk: Meggátoljuk a származtatott osztályokat abban, hogy felüldefiniálják és megváltoztassák a viselkedését, és hatékonyság végett, a fordító számára megmondjuk, hogy a metódus hívását ún. inline hívásokká alakíthatja, a hívás helyett a hívott függvény kódja be lesz másolva. Minden private metódus implicite final lesz: Nem lehet felüldefiniálni, mert privát. Ha mégis megpróbáljuk, akkor új metódust kapunk Végső osztályok Osztályra is mondhatjuk, hogy legyen végső: final class Azt jelenti, hogy
belőle nem lehet új osztályokat származtatni. Biztonsági vagy hatékonysági megfontolásból tehetjük. Minden metódus is implicite végső lesz (nem lehet származtatni, ezért felüldefiniálni sem). Ugyanazok a hatékonysági megfontolások érvényesek lesznek, mintha ki lenne írva. 4.11 Polimorfizmus A harmadik lényeges OOP tulajdonság az adatabsztrakció és öröklődés után a polimorfizmus. Pl. class Note { private int value; private Note(int val) { value = val; } public static final Note MIDDLE C = new Note(0), C SHARP = new Note(1), B FLAT = new Note(2); } class Instrument { public void play(Note n) { System.outprintln("Instrumentplay()"); } } class Wind extends Instrument { public void play(Note n) { System.outprintln( "Wind.play()"); } } public class Music { public static void tune(Instrument i) { i.play(NoteMIDDLE C); } public static void main(String[] args) { Wind flute = new Wind(); tune(flute); // Upcasting } } 2002, Beszédes
Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 95 Az előző példában a tune(flute) hívás során „elveszik” a típus, mert a metódus Instrument-et vár de Wind van átadva. Egyszerűbb lenne, ha a tune metódus Wind-et várna? Nem, mert akkor minden egyes hangszerre kellene külön tune metódus! Sőt, ha újabb hangszert szeretnénk hozzáadni, akkor mindig új metódus is kell (karbantarthatóság)! Kései kötés Honnan tudja a fordító, hogy melyik objektum play metódusát hívja? Nem tudja! Csak futás közben derül ki az objektum típusa alapján. public class Music { public static void tune(Instrument i) { i.play(NoteMIDDLE C); } public static void main(String[] args) { Wind flute = new Wind(); tune(flute); // Upcasting } } Bővíthetőség A polimorfizmusnak köszönhetően tetszőleges számú új hangszert felvehetünk a tune metódus megváltoztatása nélkül. Egy jól megtervezett OO programban ezt a
modellt követik, vagyis az objektumok az interfészek segítségével kommunikálnak egymással. 4.111 Absztrakt osztályok és metódusok Az előző példában az Instrument ősosztályban szereplő metódusok valójában „álmetódusok”. Ha ezek véletlenül meghívódnak, akkor valami hiba van a programban, mert ezeknek a szerepe az, hogy interfész legyen. Lehetne programfutás közben kiíratni, hogy hiba van, de ennek az ellenőrzéséhez sok tesztelés szükséges. Magából az ősosztályból valójában nem kellene, hogy létrejöjjön objektum. Ezért bevezették az absztrakt osztályokat és metódusokat (fordítási időbeni ellenőrzés) Kulcsszó: abstract Pl. abstract class Instrument {/*.*/} Ez az osztály nem példányosítható (fordítási hiba keletkezne) és közös interfészt biztosít a leszármazottaknak Absztrakt metódus is létezik, csak deklarációja van (nincs definíciója – törzse): abstract void f(); Ha egy osztálynak van legalább egy
absztrakt metódusa, akkor neki is absztraktnak kell lennie, de egy osztály lehet absztrakt akkor is, ha nincs absztrakt metódusa. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 96 Interfészek és belső osztályok 4.12 4.112 Futás közbeni típusazonosítás (RTTI – Run-time type identification) Upcast (ősre konvertálás) esetén elveszítjük a konkrét típust, de a konverzió biztonságos. Downcast (leszármazottra konvertálás) esetében jó lenne visszanyerni a konkrét típust. A konverzió egyéb nyelvekben nem biztonságos, de Java-ban ellenőrizve van (RTTI): Ha nem megfelelő típusra próbálunk downcast-olni, akkor egy ClassCastException típusú kivétel dobódik. 4.12 Interfészek és belső osztályok 4.121 Interfészek Az interfész osztályok a teljesen absztrakt osztályok. Kulcsszó: interface Minden metódusnak csak deklarációja lehet (törzs nélküli) és impliciten publikus. A mezők impliciten
static-ok és final-ok. Ez lehetővé teszi a „többszörös öröklődés”-t. Csak egy formát ad meg implementáció nélkül, egy „protokoll”-t valósít meg osztályok között. Az implements kulcsszóval lehet származtatni belőle (implementálni). Pl. interface Instrument { int i = 5; // static & final void play(); // Automatically public String what(); void adjust(); } class Wind implements Instrument { public void play() { System.outprintln("Windplay()"); } public String what() { return "Wind"; } public void adjust() {} } Interfészt vagy absztrakt osztályt használjunk? Ha ősosztályt készítünk, akkor mindig kezdjük azzal, hogy interfész lesz. Ha mégis szükség lesz az ősosztály szintjén metódusokat implementálni, vagy mezőket hozzáadni, akkor módosítsuk absztrakt osztályra (esetleg konkrét osztályra). 4.122 „Többszörös öröklődés” Mivel az interfészek nem foglalnak tárterületet, lehet ővé válik a
biztonságos többszörös öröklődés. Nem „igazi” többszörös öröklődésről van szó (mint pl a C++-ban). Maximum egy db „igazi” osztály lehet az ősök között, a többi interfész kell, hogy legyen. Interfész osztályok is származhatnak egymásból, ezzel új interfészt kapunk. Pl. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 97 interface CanFight { void fight(); } interface CanSwim { void swim(); } interface CanFly { void fly(); } class ActionCharacter { public void fight() {} } class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { public void swim() {} public void fly() {} } 4.123 Konstansok csoportosítása Mivel az interfészek mezői automatikusan static-ok és final-ok, jó eszköz arra, hogy konstansokat csoportosítsunk (hasonlít a C++-os Enum-ra). Pl. public interface Months { int JANUARY = 1, FEBRUARY = 2, MARCH = 3, APRIL = 4, MAY = 5, JUNE = 6,
JULY = 7, AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10, NOVEMBER = 11, DECEMBER = 12; } public final class Test { public static void main(String[] args) { System.outprintln(MonthsMAY); } } 4.124 Belső osztályok (Inner classes) A belső osztályok az osztályon- vagy metóduson belüli osztályok. Hasznos lehet, ha logikailag összetartozó osztályokat csoportosítunk, vagy máshól nem használt algoritmust szeretnénk elrejteni a külvilágtól. A belső osztályból elérhetőek a „körülvevő” osztály elemei (hacsak nem static a belső osztály). Fontos kihangsúlyozni, hogy ez nem kompozíció! .class fájlnév: OuterClass$InnerClassclass Pl. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 98 4.13 Objektumok tárolása: Kollekciók (konténerek) public class Parcel { class Contents { private int i = 11; public int value() { return i; } } class Destination { private String label; Destination(String whereTo) { label =
whereTo; } String readLabel() { return label; } } public void ship(String dest) { Contents c = new Contents(); Destination d = new Destination(dest); System.outprintln(dreadLabel()); } public static void main(String[] args) { Parcel1 p = new Parcel1(); p.ship("Tanzania"); } } 4.13 Objektumok tárolása: Kollekciók (konténerek) Csak nagyon egyszerű program esetében van előre meghatározott számú objektum ismert élettartammal. Általában ez csak futási időben derül ki Kell, hogy legyen lehetőség objektumokat létrehozni bármikor, bárhol. Ezeknek nem lehet mindig nevet adni. A megoldás: Kollekciók (konténerek) használata. Ilyenek: 4.131 • Beépített típus: tömb (array) • Konténer osztályok a java.util könyvtárból Tömbök Miért jobb a tömb a többi konténernél? • Hatékonyság: Konstans idejű az elérés, de fix a méret (flexibilis tömb: ArrayList konténer). • Típus: Nem veszíti el a típust – adott típusú elemeket
tárol, nem Object-eket. Először mindig próbáljunk meg tömböt használni, és csak akkor válasszunk másik konténert, ha korlátba ütközünk Az Arrays osztály Tömbműveletekhez ad segítséget az Arrays osztály, ami a java.util könyvtár része. Statikus metódusokat tartalmaz: • equals() – tömbök egyenlősége • fill() – tömb feltöltése adott értékkel • sort() – tömb rendezése • binarySearch() – tömbben keresés • asList() – List típusú listát készít belőle (később) 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 99 Mindegyik meg van valósítva (overload) minden primitív típusra és az Objectre. Tömbök egyenlősége, feltöltése Az Arrays osztály részei equals() – Az egész tömböt összehasonlítja, elemenként fill() – Tömb feltöltése adott értékkel Példa: public class ComparingArrays { public static void main(String[]
args) { int[] a1 = new int[10]; int[] a2 = new int[10]; Arrays.fill(a1, 47); Arrays.fill(a2, 47); System.outprintln(Arraysequals(a1,a2)); } } Tömbök rendezése Az általános rendezőalgoritmusoknak az elemek típusaitól függetleneknek kell lenniük, vagyis össze kell, hogy tudjon hasonlítani két elemet típustól függetlenül. Ez ún. Callback technikával oldható meg: Az elem saját maga hasonlítja magát össze egy ugyanolyan típusú másik elemmel. Két módszer van rá: Comparable és Comparator. A „természetes módszer” a Comparable. Ilyenkor a javalangComparable interfészt kell implementálni. Ennek egyetlen metódusa van: compareTo() Ez egy objektumot vár paraméterül, a visszatérési értéke pedig: <0, ha az aktuális objektum kisebb, mint az argumentum; =0 ha egyenlő és >0 ha nagyobb, mint az argumentum. Pl. public class CompType implements Comparable { int i; public CompType(int n) { i = n; } public int compareTo(Object rv) { int rvi =
((CompType)rv).i; return (i < rvi ? -1 : (i == rvi ? 0 : 1)); } public static void main(String[] args) { CompType[] a = new CompType[10]; /* fill the array / Arrays.sort(a); } } A másik módszer a Comparator. Akkor használatos, ha nem implementálta az osztály a Comparable interfészt. Ilyenkor egy külön osztályt kell létrehozni, ami a java.langComparator interfészt implementálja Ennek két metódusa van: compare() és equals(). Általában csak a compare()-t kell implementálni, mert az Object ősosztály implementálja a másikat. Két objektumot vár paraméterül. A visszatérési érték: <0, ha az első argumentum kisebb; =0 ha egyenlő és >0 ha nagyobb mint a második objektum. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 100 4.13 Objektumok tárolása: Kollekciók (konténerek) Pl. class CompTypeComparator implements Comparator { public int compare(Object o1, Object o2) { int i1 = ((CompType)o1).i; int
i2 = ((CompType)o2).i; return (i1 < i2 ? -1 : (i1 == i2 ? 0 : 1)); } } public class ComparatorTest { public static void main(String[] args) { CompType[] a = new CompType[10]; /* fill the array / Arrays.sort(a, new CompTypeComparator()); } } Tömbben keresés Csak rendezett tömbben lehet keresni! Arrays.binarySearch() – bináris keresés Visszatérési érték: ≥0 ha megtalálta (a keresett elem tömbindexe); <0 ha nem (melyik elem előtt kellene, hogy legyen). Ha több elem is megfelel a keresésnek, akkor bizonytalan, hogy melyiket találja meg (ilyenkor használjunk inkább TreeSet-et, lásd később). Ha Comparator-ral lett rendezve, akkor meg kell adni ugyanazt a Comparator objektumot a keresésnek is. Pl. public class ArraySearching { public static void main(String[] args) { int[] a = new int[100]; /* fill the array / Arrays.sort(a); /*.*/ int location = Arrays.binarySearch(a,19); if(location >= 0) { /* element found / } } } Tömbök másolása System.arraycopy()
statikus metódus Meg van valósítva minden primitív típusra és az Object-re (csak a referenciák másolódnak!) public class CopyingArrays { public static void main(String[] args) { int[] i = new int[25]; int[] j = new int[25]; /*.*/ System.arraycopy(i, 0, j, 0, ilength); /*.*/ } } 4.132 Kollekciók (konténerek) A konténer osztálykönyvtár egyike a leghatékonyabb programozási eszközöknek 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 101 Java 2-ben teljesen újratervezték (a Java 1.x verziókban nagyon gyengére sikeredett) Két koncepció: • Kollekció (Collection): Egyéni elemek csoportja, általában valamilyen szabályossággal (listában sorrendiség, halmazban egyediség, stb.) • Leképezés (Map): Kulcs-adat kettősök csoportja, a kulcsra gyors keresést biztosít Pl. public class PrintingContainers { static Collection fill(Collection c) { c.add("dog");
c.add("dog"); c.add("cat"); return c; } static Map fill(Map m) { m.put("dog", "Bosco"); m.put("dog", "Spot"); m.put("cat", "Rags"); return m; } public static void main(String[] args) { System.outprintln(fill(new ArrayList())); System.outprintln(fill(new HashSet())); System.outprintln(fill(new HashMap())); } } Output: [dog, dog, cat] [cat, dog] {cat=Rags, dog=Spot} A konténerek hátránya Elveszítik a típust • Mindig Object-eket tárolnak • Általános célú konténerek: Nem tárolhatnak specifikus típusú objektumokat Bármi belerakható – nincs típusellenőrzés • Pl. egy macskákat tároló konténerbe nyugodtan bele lehet tenni kutyákat (csak az Object referenciát teszi bele) Downcast-olni kell az elemet használat előtt! 4.133 Iterátorok Minden konténernek támogatnia kell az elemek hozzáadását, illetve elérését. Pl. ArrayList esetében ez könnyű: add(); get() Ennél
általánosabb, absztraktabb megoldás kell. Generikus kódot szeretnénk írni, ami nem függ a használt konténertől (ne kelljen újraírni). A megoldás az iterátorok használata. Egy iterátor egy objektum, ami végighalad egy objektumsorozaton anélkül, hogy a felhasználó programozó tudna róla, hogy milyen konkrét belső struktúrája van a konténernek. A Java iterátor tudása: • Kérni lehet egy Iterator objektumot a konténert ől az iterator() metódushívással Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 102 4.13 Objektumok tárolása: Kollekciók (konténerek) • Kérni lehet a következő elemet a next() metódussal (a legelsőt is next()-tel kérjük!) • Meg lehet kérdezni, hogy maradt-e még elem a sorozatban a hasNext() metódussal • Törölni lehet az iterátorral lekért elemet remove()-val Pl. import java.util*; class Hamster { private int hamsterNumber; Hamster(int i) { hamsterNumber =
i; } public String toString() { return "This is Hamster #" + hamsterNumber; } } class Printer { static void printAll(Iterator e) { while(e.hasNext()) System.outprintln(enext()); } } public class HamsterMaze { public static void main(String[] args) { ArrayList v = new ArrayList(); for(int i = 0; i < 3; i++) v.add(new Hamster(i)); Printer.printAll(viterator()); } } 4.134 Output: This is Hamster #0 This is Hamster #1 This is Hamster #2 Konténer osztályhierarchia Iterator Collection Produces Map Produces ListIterator Produces HashMap List TreeMap Set WeakHashMap ArrayList LinkedList HashSet TreeSet Utilities Collections Comparable 4.135 Comparator Arrays Collection (List, Set) funkcionalitása boolean add(Object) – elem hozzáadása boolean addAll(Collection) – elemek hozzáadása void clear() – töröl minden elemet boolean contains(Object) – true, ha tartalmazza az elemet 2002, Beszédes Árpád és Ferenc Rudolf
Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 103 boolean containsAll(Collection)– boolean isEmpty() – true, ha üres Iterator iterator() – visszaad egy iterátort boolean remove(Object) – elem törlése boolean removeAll(Collection) – minden elem törlése boolean retainAll(Collection) – csak akkor tartja meg az elemet, ha az szerepel a paraméterben (metszet) int size() – elemek száma Object[] toArray() – felépít egy tömböt, ami tartalmazza a konténer elemeit Object[] toArray(Object[] a) 4.136 true, ha tartalmazza az elemeket – mint az előző, csak szűri típus szerint: csak az a paraméterben kapott típusú elemeknek megfelelő típusú elemekből építi fel a tömböt Lista List (absztrakt) – Elemeket tárol adott sorrendben. Oda-vissza be lehet járni a listát, és be lehet szúrni elemeket a listába elemek közé is a ListIterator segítségével. Két féle megvalósítása van: 4.137 •
ArrayList – Tömbbel megvalósított lista: Gyors elemelérés de lassú beszúrás/törlés, és • LinkedList – „Igazi” láncolt lista: Gyors beszúrás/törlés de lassú elemelérés. Plusz metódusokat tartalmaz, hogy használható legyen stack-, queue- és deque-ként (addFirst(), addLast(), getFirst(), getLast(), removeFirst(), removeLast()). Halmaz Set (absztrakt) – Egyedi elemeket tárol sorrendiség nélkül. A tárolt elemeknek definiálniuk kell az equals() metódust. Két változata van: 4.138 • HashSet – Gyors keresést biztosító halmaz (a tárolt elemeknek definiálniuk kell a hashCode() metódust) • TreeSet – Rendezett halmaz (first() – a legkisebb elem; last() – a legnagyobb elem; subSet(from,to), headSet(to), tailSet(from) – visszaadja a fának egy részét) Map funkcionalitása void clear() – töröl minden elemet boolean containsKey(Object) – true, ha tartalmazza a kapott kulcsú elemet boolean containsValue(Object) –
true, ha tartalmazza a kapott tartalmú elemet Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 104 4.14 Hibakezelés kivételekkel Set entrySet() – visszaadja halmazként a tárolt adatokat Object get(Object key) – a kapott kulcsú elemet adja vissza boolean isEmpty() – true, ha üres Set keySet() – visszaadja halmazként a tárolt kulcsokat Object put(Object key, – beszúr egy új elemet (párost) Object value) 4.139 void putAll(Map) – beszúrja a kapott Map elemeit (másolás) Object remove (Object key) – törli a kapott kulcsú elemet int size() – elemek száma Collection values() – visszaadja kollekcióként a tárolt elemeket Leképezés Map (leképezés) – Kulcs-adat objektum-párok csoportja, a kulcsra gyors keresést biztosít. Két változata van: 4.14 • HashMap – Hash-táblával implementált, keresésre/beszúrásra optimalizálva. • TreeMap – Piros-fekete fával
implementált, rendezetten tárolja az elemeket (firstKey() – a legkisebb kulcs; lastKey() – a legnagyobb kulcs; subMap(from,to), headMap(from), tailMap(to) – visszaadja a fának egy részét). Hibakezelés kivételekkel Java alapfilozófia: Rosszul formált kód nem fog futni. Ideális lenne, ha minden hibát fordítási időben ki lehetne deríteni, de a valóságban nem így van. Korábbi nyelvekben (pl. C-ben) a hibakezelés inkább csak konvenció volt: Tipikusan, a függvény visszaad egy hibakódot, vagy beállít egy jelzőt, amit a hívó fél kell(ene), hogy ellenőrizzen, de ez általában elmarad: pl. ki ellenőrzi le, hogy a printf() mit ad vissza? A megoldás: Nyelvi szinten beépíteni és megkövetelni a hibakezelést. Ez nem a Java találmánya, már az 1960-as években ismerték operációs rendszer szinten. A Java kivételkezelése a C++-on alapul (esetleg az Object Pascal-on), a C++ kivételkezelése pedig az Ada-n. A kivételekkel történő hibakezelés
még egy jelentős előnyhöz juttat: „Megtisztul” a kód, mert különválik az „igazi” kód a hibakezelést ől. 4.141 Kivételek Kivételes feltétel: egy probléma, ami meggátolja a programfutást egy adott metódusban (blokkban). Egyszerű példa a null referencia: Érdemes leellenőrizni és kivételt „dobni” ahelyett, hogy folytatnánk: if (p == 0) throw new NullPointerException(); 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 105 Kivétel dobáskor (throw) egy kivétel objektum jön létre a heap-en. Az aktuális programvégrehajtás megáll és a kivétel objektum referenciája „kidobódik”. A kivételkezelő mechanizmus veszi át az irányítást és keres egy megfelelő kivételkezelőt, ami lekezeli a hibát és folytatja a program végrehajtását. 4.142 Kivételek argumentumai Minden beépített kivételnek két konstruktora van: a default, és ami egy string-et vár
(ezt később fel lehet használni) if (p == 0) throw new NullPointerException(”p is not initialized!”); A kivételkezelés valójában egy alternatív „return” mechanizmus: A kivétel objektum van „visszaadva” (a metódus visszatérési típusa más!). Nem biztos, hogy csak egy szinttel tér vissza, hanem addig, amíg valaki nem kezeli le a kivételt. 4.143 Kivétel elkapása Ha valahol el lett dobva egy kivétel, akkor az feltételezi, hogy azt valahol máshol „elkapják”. Védett régió: A kód olyan része, ami kivételeket hozhat létre, és amit a hibakezelő kód követ. Pl. try { // „normál kód”, amiben kivétel keletkezhet // (védett régió) } catch(ExceptionType1 e1) { // hibakezelő kód az e1 kivételre } catch(ExceptionType2 e2) { // hibakezelő kód az e2 kivételre throw e2; // tovább is lehet dobni } catch(Exception e) { // hibakezelő kód az összes (megmaradt) kivételre } 4.144 Saját kivételek Saját kivételeket is lehet írni,
származtatni kell valamelyik létező kivétel osztályból (pl. Exception (ős)osztályból) Pl. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 106 4.15 Egyéb Java technológiák class SimpleException extends Exception { } public class SimpleExceptionDemo { public void f() throws SimpleException { System.outprintln( "Throwing SimpleException from f()"); throw new SimpleException(); } public static void main(String[] args) { SimpleExceptionDemo sed = new SimpleExceptionDemo(); try { sed.f(); } catch(SimpleException e) { System.errprintln("Caught it!"); } } } Kivétel specifikáció Java-ban meg kell adni, hogy egy metódus milyen kivételeket dobhat a throws kulcsszóval (ez része a metódus-deklarációnak). Pl. void f() throws SimpleException, NullPointerException { // . } 4.146 Java standard kivételek Ősosztály: Throwable, két leszármazottja van: • Error: általában nem kell vele foglalkozni
(fordítási időbeni- és rendszer hibákat képvisel), és • Exception: a Java programozó számára ez az „ősosztály”. A kivételek részletes leírása megtalálható a Java http://java.suncom/j2se/13/docs/api/java/lang/Exceptionhtml 4.147 honlapján: RuntimeException Java standard kivétel, amit nem kell külön megadni a kivétel specifikációban. Azon kivételek őse, amiket a virtuális gép dobhat normális működés közben, pl.: NullPointerException, ClassCastException, IndexOutOfBoundsException, stb. 4.15 Egyéb Java technológiák 4.151 Futás közbeni típusazonosítás (RTTI) és a reflection mechanizmus A futás közbeni típusazonosítás (RTTI) lehetővé teszi, hogy megállapítsuk egy objektum valódi típusát akkor, amikor csak az ősére mutató referenciánk van. A Class osztály Osztályokról hordoz információt (ún. meta-osztály) Ez az osztály van használva az összes többi osztály létrehozására. 2002, Beszédes Árpád és
Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 107 A program minden osztályához tartozik egy Class objektum, ami fordításkor jön létre és a megfelelő class fájlba el van tárolva. Futási időben a virtuális gép ellenőrzi, hogy a szükséges Class objektum be van-e már töltve, és ha nem, akkor betölti azt. Egy Java program futtatásakor nem töltődik be teljes egészében a memóriába, hanem mindig csak a szükséges részek (osztályok). Ha a megfelelő típushoz tartozó Class objektum be lett töltve, akkor abból jön létre az összes ilyen típusú objektum (hasonló a klónozáshoz). Minden objektum tartalmaz egy referenciát az ő Class objektumára, ami tartalmazza többek között a típus konkrét nevét. Class.forName Statikus metódus, segítségével lekérhet ő osztálynév alapján a Class objektum referenciája. Pl Class c = ClassforName(”MyClass”); Class literal Egy konkrét objektum Class
objektum referenciája elérhető direkt is (statikus attribútum): Pl. Class c = MyClassclass; instanceof kulcsszó Leellenőrizhető, hogy egy objektum adott típusú-e: Pl. if (myObj instanceof MyClass) . Ugyanez elérhető a Class objektum isInstance metódusával is: if (MyClass.classisInstance(myObj)) Reflection A futás közbeni típusazonosítás csak akkor működik, ha ismert a típus már fordítási időben. Java-ban viszont használhatunk olyan (ismeretlen) osztályokat is, amelyek nem is részei a mi programunknak (pl. utólag letöltődik a hálózatról) java.langreflect csomag A következő osztályokat tartalmazza: Field, Method és Constructor (mind a Member interfészt implementálja). A virtuális gép hozza létre a megfelelő objektumokat belőlük, amik az ismeretlen osztály megfelelő tagjait képviselik. Ezek után használhatjuk a Constructor-t, hogy új objektumokat hozzunk létre az ismeretlen osztályból, használhatjuk a get() és set() metódusokat,
hogy írjuk/olvassuk a Field objektumok által képviselt attribútumok értékét, és az invoke() metódust, hogy meghívjuk a Method objektumok által képviselt metódusokat. Pl. A MyClass osztály metódusainak és konstruktorainak kiíratása: Class c = Class.forName(”MyClass”); Method[] m = c.getMethods(); Constructor[] ctor = c.getConstructors(); for (int i = 0; i < m.length; i++) System.outprintln(m[i]); for (int i = 0; i < ctor.length; i++) System.outprintln(ctor[i]); Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 108 4.152 4.15 Egyéb Java technológiák Távoli metódushívás (RMI) Lehetővé teszi programkód futtatását több gépen megosztva a hálózatban. A távoli gépen élhetnek objektumok, amiknek üzenni lehet és visszaadják az eredményt. Erősen kötődik interfészek használatához. A távoli objektumot csak interfész referenciával használhatjuk és csak ezen keresztül kommunikálhatunk
vele. A következő négy szabályt be kell tartani. • A távoli interfésznek publikusnak kell lennie. • A távoli interfésznek bővítenie kell a java.rmiRemote interfészt • A távoli interfész minden metódusának java.rmiRemoteException típust • A távoli objektumra csak a távoli interfészszel hivatkozhatunk (pl. ha átadjuk argumentumként vagy visszatérési értékként). tudnia kell dobni a Pl. időszerver: // PerfectTime távoli interfész import java.rmi*; public interface PerfectTimeI extends Remote { long getPerfectTime() throws RemoteException; } A távoli interfész implementációja A szervernek tartalmaznia kell egy osztályt, ami UnicastRemoteObject-et, és implementálja a távoli interfészt. bővíti az Pl. // PerfectTime távoli objektum implementációja import java.rmi*; import java.rmiserver*; import java.rmiregistry*; import java.net*; public class PerfectTime extends UnicastRemoteObject implements PerfectTimeI { public long
getPerfectTime() throws RemoteException { return System.currentTimeMillis(); } // Implementálni kell a konstructort a throw miatt public PerfectTime() throws RemoteException { // super(); // automatikus } // RMI regisztráció (szerver beüzemelése) public static void main(String[] args) throws Exception { System.setSecurityManager(new RMISecurityManager()); PerfectTime pt = new PerfectTime(); Naming.bind("//mymachine:2005/PerfectTime", pt); System.outprintln("Ready to do time"); } } • Be kell üzemelni a RMISecurityManager • Létre kell hozni a távoli objektumo(ka)t. • Regisztrálni kell a távoli objektumo(ka)t. 2002, Beszédes Árpád és Ferenc Rudolf biztonságkezelőt (security manager): Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 109 A Naming.bind híváshoz futnia kell a gépen egy külön processzben a registrynek • Windows-ban: start rmiregistry • Unix-ban: rmiregistry & Ha nem
felel meg az alapértelmezett port (1099), akkor meg kell adni a fenti parancssorban (pl. a példában 2005) Ezek után le kell futtatni az rmic programot a lefordított kódra: rmic PerfectTime Ennek eredményeképpen két új fájl keletkezik: PerfectTime Stub.class PerfectTime Skel.class Példaprogram, ami használja a fenti távoli objektumokat: import java.rmi*; import java.rmiregistry*; public class DisplayPerfectTime { public static void main(String[] args) throws Exception { System.setSecurityManager(new RMISecurityManager()); PerfectTimeI t = (PerfectTimeI)Naming.lookup("// mymachine:2005/PerfectTime"); for (int i = 0; i < 10; i++) System.outprintln("Perfect time = " + tgetPerfectTime()); } } ///:~ 4.153 Common Object Request Broker Architecture (CORBA) A Java rendkívül alkalmas a CORBA implementációjára, ezért a CORBA alapvet ő működését e nyelv segítségével mutatjuk be. (A Java applet-ek is kitűnően jól működhetnek mint CORBA
kliensek.) A CORBA technológia részletes leírása a 3.2 részben van megadva A példában egy valamely gépen futó szervert valósítunk meg, amelyet a pontos idő lekérésére használhatunk. Azt is bemutatjuk, hogy hogyan lehet egy ilyen lekérdező klienst megvalósítani. Mindkettő Javaban lesz megvalósítva, de természetesen tetszőleges másik nyelvet is lehet választani. IDL forrás elkészítése. Az első lépés megírni a szolgáltatásunkhoz az IDL forrású interfészt, amelyet azután tetszőleges nyelvre le lehet fordítani és implementálni. Ezt a forrást a kliens és a szerver oldalon is elérhetővé kell tenni, hiszen ez lesz a két végpont között a kapcsolat. Az ExactTime szerver IDL forrása a következő: module remotetime { interface ExactTime { string getTime(); }; }; Amint látható, maga az IDL leírás rendkívül egyszerű, az ExactTime nevű interface a remotetime névtéren belül helyezkedik el, és egy metódusa van, amely egy sztringet ad
vissza. Fordítás. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 110 4.15 Egyéb Java technológiák A második lépés a fenti IDL forrást lefordítani (Java esetében erre használható az idltojava program). Fordítás után létrejönnek a Java nyelvű források, amelyek a stub-ot és a skeleton-t tartalmazzák. Itt néhány forrásfájl keletkezik: a szerver és a kliens által használható ExactTimeImplBase nevű skeleton és ExactTimeStub nevű stub osztályok, maga az interfész Java nyelven, valamint egyéb kiegészítő osztályok. A szerver és a kliens megvalósítása. A fenti alaposztályokat felhasználva mint ősosztályt, megvalósíthatjuk a szervert és a klienst. Alább láthatjuk a szervert megvalósító ExactTimeServer osztály, valamint a RemoteTimeServer alkalmazást: import import import import import import remotetime.*; org.omgCosNaming*; org.omgCosNamingNamingContextPackage*; org.omgCORBA*;
java.util*; java.text*; class ExactTimeServer extends ExactTimeImplBase { public String getTime(){ return DateFormat. getTimeInstance(DateFormat.FULL) format(new Date(System.currentTimeMillis())); } } public class RemoteTimeServer { public static void main(String[] args) throws Exception { ORB orb = ORB.init(args, null); ExactTimeServer timeServerObjRef = new ExactTimeServer(); orb.connect(timeServerObjRef); org.omgCORBAObject objRef = orb.resolve initial references("NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); NameComponent nc = new NameComponent("ExactTime", ""); NameComponent[] path = { nc }; ncRef.rebind(path, timeServerObjRef); java.langObject sync = new java.langObject(); synchronized(sync){ sync.wait(); } } } Látható, hogy a szerver servant osztály megvalósítása rendkívül egyszerű, míg az ORB és egyéb CORBA szolgáltatások használata a szervert működtető alkalmazásban kicsit bonyolultabb. Csak
vázlatosan említve, itt a következőket kell elvégezni: • ORB létrehozása, • a konkrét szerver objektum létrehozása és bejegyzése, ezt fogja a kliens elérni, • Naming Service használata és összekötése az objektum hivatkozással, 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek • 111 és végül a szerver készenléti állapotba helyezése, amikor a kliens kéréseire várakozik. A kliens megvalósítását pl. a következőképpen tehetjük: import remotetime.*; import org.omgCosNaming*; import org.omgCORBA*; public class RemoteTimeClient { public static void main(String[] args) throws Exception { ORB orb = ORB.init(args, null); org.omgCORBAObject objRef = orb.resolve initial references("NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); NameComponent nc = new NameComponent("ExactTime", ""); NameComponent[] path = { nc };
ExactTime timeObjRef = ExactTimeHelper.narrow(ncRefresolve(path)); String exactTime = timeObjRef.getTime(); System.outprintln(exactTime); } } A kliens alkalmazásban is először néhány adminisztratív dolgot kell elvégezni, majd a Naming Service segítségével elkérjük a kívánt szerver-objektumra a referenciát. Ezt felhasználva már meg is hívhatjuk az interfésznek megfelelő metódust, megkapva a pontos időt a szervertől. Aktiválás. Miután a fenti programok elkészültek, már csak annyi a dolgunk, hogy elindítsuk először a szerver alkalmazást, majd azután a kliens program végrehajtásával megkapjuk a pontos időt. Mielőtt azonban a rendszer működhetne, szükség van a használt CORBA szolgáltatásokra, nevezetesen a Naming Servicenek működnie kell. A mi esetünkben ez a Java virtuális gépben fut és a hálózatot folyamatosan figyeli. A kliens működése nagyon egyszerűnek tűnik, hiszen a timeObjRef.getTime(); utasítás úgy néz ki, mint egy
lokális metódushívás, pedig ez alatt egy bonyolult mechanizmus működik! Csak megjegyzésképpen, a fenti példa a Java RMI segítségével is megvalósítható (ld. az előző, 4152 részt), de a lényeges különbség hogy az IDL forrásban megírt interfészt felhasználva tetszőleges nyelven lehetne az implementációkat megírni, nem csak Java-ban mint az RMI esetében. Igaz, Java-ban ez is megoldható lenne úgy, hogy valamilyen Java nyelvű wrapper-t írunk a nem Java nyelvű implementáció köré és használjuk az RMI-t, csakhogy pontosan ez az a dolog, ami a CORBA-nak az alapvető feladata! 4.154 Enterprise Java Beans (EJB) Az EJB specifikáció szerver oldali komponens modellt ír le. Különböző szerepeket definiál egy nagyméretű üzleti alkalmazás létrehozatalához: • EJB ellátó (EJB provider) –Általános, újrafelhasználható EJB komponenseket létrehozó szoftvergyártó. Ezek a komponensek speciális jar fájlba vannak csomagolva (ejb-jar
fájl). • Alkalmazás összeállító (Application assembler) – Alkalmazásokat állít elő ejb-jar fájlok segítségével. Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 112 4.15 Egyéb Java technológiák • Telepítő (Deployer) – Futtató környezetbe (EJB tároló – EJB container) illeszti a fenti ejb-jar fájlokat és alkalmazásokat. • EJB tároló/szerver ellátó (EJB container/server provider) – Futtató környezetet biztosít. • Rendszergazda (System administrator) – Kezeli, irányítja a különböző komponenseket és szolgáltatásokat. EJB komponensek Az EJB komponensek az üzleti logikák újrafelhasználható részei, amiknek szigorú szabályoknak és tervezési mintáknak kell megfelelniük. Részei: • Enterprise Bean – Egy Java osztály, ami egy ·Enterprise Bean interfészt implementál és bizonyos üzleti műveleteket valósít meg. • Helyi interfész (Home interface) –
Minden Enterprise Bean-hez kell egy hozzárendelt interfész, amivel a kliensek megtalálhatják, illetve létrehozhatják. • Távoli interfész (Remote interface) – Java interfész, ami a Java Bean publikus metódusait tükrözi. Hasonló szerepe van, mint a CORBA IDL interfésznek • Telepítési leírás (Deployment descriptor) – XML fájl, ami információt hordoz az adott EJB-ről. • EJB-Jar fájl – Egyszerű jar fájl, ami tartalmazza magát az EJB-t, a helyi- és távoli interfészt és a telepítési leírást. EJB működése Az EJB tároló implementálja a helyi- és távoli interfészt. Ő felel az EJB életciklusáért. Az EJB-hez érkező minden kérés a tárolón keresztül mehet csak át EJB kategóriák Viselkedésük alapján két féle EJB-t különböztetünk meg: • Esemény Bean – Adatokon való műveletek valósítanak meg • Egyed Bean – Az adatokat képviselik Példa EJB Az előzőekben (RMI) ismertetett időszerver példát
implementáljuk EJB komponensként. Távoli interfész: A következő szabályokat be kell tartani. • A távoli interfésznek publikusnak kell lennie. • A távoli interfésznek bővítenie kell a javax.ejbEJBObject interfészt • A távoli interfész minden metódusának java.rmiRemoteException típust • Minden objektum, amit pl. argumentumként át lehet adni, érvényes RMI-IIOP adattípusnak kell lennie. 2002, Beszédes Árpád és Ferenc Rudolf tudnia kell dobni a Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 113 // PerfectTimeBean távoli interfész import java.rmi*; import javax.ejb*; public interface PerfectTime extends EJBObject { public long getPerfectTime() throws RemoteException; } Helyi interfész: A következő szabályokat be kell tartani. • A helyi interfésznek publikusnak kell lennie. • A helyi interfésznek bővítenie kell a javax.ejbEJBHome interfészt • A távoli interfész minden create
metódusának tudnia kell dobni a java.rmiRemoteException és javaxejbCreateException típusokat. • A create metódus visszatérési értéke távoli interfész kell, hogy legyen. • A finder metódus visszatérési értéke távoli interfész, java.utilEnumeration vagy javautilEnumeration kell, hogy legyen entitás Bean-ek esetében. • Minden objektum, amit pl. argumentumként át lehet adni, érvényes RMI-IIOP adattípusnak kell lennie. // PerfectTimeBean helyi interfész import java.rmi*; import javax.ejb*; public interface PerfectTimeHome extends EJBHome { public PerfectTime create() throws CreateException, RemoteException; } EJB implementáció (üzleti logika): A következő szabályokat be kell tartani. • Az osztálynak publikusnak kell lennie. • Az osztálynak implementálnia kell egy EJB interfészt (javax.ejbSessionBean vagy javaxejbEntityBean) • Definiálnia kell a távoli interfészben levő metódusoknak megfelelő metódusokat. • Egy vagy több
ejbCreate() metódust inicializálásához. • Minden objektum, amit pl. argumentumként át lehet adni, érvényes RMI-IIOP adattípusnak kell lennie. kell definiálnia az EJB import java.rmi*; import javax.ejb*; public class PerfectTimeBean implements SessionBean { private SessionContext sessionContext; public long getPerfectTime() { return System.currentTimeMillis(); } // EJB metódusok public void ejbCreate() throws CreateException {} public void ejbRemove() {} public void ejbActivate() {} Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 114 4.15 Egyéb Java technológiák public void ejbPassivate() {} public void setSessionContext(SessionContext ctx) { sessionContext = ctx; } } Ezek után létre kell hozni a telepítési leírást (deployment descriptor): <?xml version="1.0" encoding="Cp1252"?> <!DOCTYPE ejb-jar PUBLIC -//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 11//EN
http://java.suncom/j2ee/dtds/ejb-jar 1 1dtd> <ejb-jar> <description>Example for Chapter 15</description> <display-name></display-name> <small-icon></small-icon> <large-icon></large-icon> <enterprise-beans> <session> <ejb-name>PerfectTime</ejb-name> <home>PerfectTimeHome</home> <remote>PerfectTime</remote> <ejb-class>PerfectTimeBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> <ejb-client-jar></ejb-client-jar> </ejb-jar> Példa kliensre: // Kliens program a PerfectTimeBean használatára public class PerfectTimeClient { public static void main(String[] args) throws Exception { javax.namingContext context = new javax.namingInitialContext(); Object ref = context.lookup("perfectTime"); PerfectTimeHome home =
(PerfectTimeHome)javax.rmiPortableRemoteObjectnarrow( ref, PerfectTimeHome.class); PerfectTime pt = home.create(); System.outprintln("Perfect Time EJB invoked, time is: " + pt.getPerfectTime()); } } ///:~ 4.155 JNI – Java Native Interface Habár a Java egy teljes nyelv, szükség lehet rá, hogy egyéb nyelveken íródott programkódot is használjunk (pl. sebesség-kritikus alkalmazásoknál, vagy már meg van írva és nem szeretnénk újraírni Java-ban). Jelenleg csak a C és C++ nyelvek vannak támogatva. Kulcsszó: native Pl. Natív metódus hívása A ShowMessage egy natív metódus, ami meg fogja hívni a printf() C könyvtári függvényt: ShowMessage.java: public class ShowMessage { 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok 4 Objektumorientált nyelvek 115 private native void ShowMessage(String msg); static { System.loadLibrary("MsgImpl"); } public static void main(String[] args) { ShowMessage
app = new ShowMessage(); app.ShowMessage("Generated with JNI"); } A System.loadLibrary("MsgImpl") hívás betölt egy DLL-t (lásd később). Ezek után a lefordított class fájlból C header-t kell generalni a javah programmal: javah ShowMessage Ennek az eredménye (kommentek törölve): ShowMessage.h: #include <jni.h> #ifndef Included ShowMessage #define Included ShowMessage #ifdef cplusplus extern "C" { #endif JNIEXPORT void JNICALL Java ShowMessage ShowMessage (JNIEnv *, jobject, jstring); #ifdef cplusplus } #endif #endif Ezek után meg kell írni a programkódot, ami megvalósítja a Java ShowMessage ShowMessage natív metódust, majd le kell fordítani a fenti DLL-be (a példában MsgImpl.dll) ShowMessage.cpp: #include <jni.h> #include <stdio.h> #include "ShowMessage.h" extern "C" JNIEXPORT void JNICALL Java ShowMessage ShowMessage(JNIEnv* env, jobject, jstring jMsg) { const char*
msg=env->GetStringUTFChars(jMsg,0); printf("Thinking in Java, JNI: %s ", msg); env->ReleaseStringUTFChars(jMsg, msg); } A natív metódus argumentumai képezik a kaput vissza a Java-ba (virtuális gépbe). JNIEnv* – segítségével visszahivatkozhatunk a virtuális gépbe jobject – lényegében a hívó Java-beli objektum this referenciája A többi paraméter az eredeti Java natív metódus paraméterei (jstring). 4.156 Java Mail A Java Mail API a Sun egyik legújabb fejlesztése, amivel könnyen lehet platformés protokoll független levelezési rendszereket készíteni kliens és szerver oldalon egyaránt. Java Mail API alaposztályok: Objektumorientált nyelvek és módszertanok 2002, Beszédes Árpád és Ferenc Rudolf 116 4.15 Egyéb Java technológiák javax.mailSession Az API belépési pontja. Innen érhet ők el az ún service provider implementációk a különböző protokollokhoz (POP3, SMTP, IMAP4). javax.mailStore Konkrét protokollhoz
valósít meg írás/olvasást, keresést és figyelést. javax.mailTransport Konkrét protokollon keresztül történő üzenetküldést valósít meg. javax.mailFolder Hierarchikus elrendezést biztosít a leveleknek, és elérhetővé teszi a leveleket javax.mailMessage típusú objektumok formájában javax.mailMessage Egy konkrét e-mail-t tesz elérhetővé. Hozzáférést biztosít minden részlethez, pl feladó címe, tárgy, stb. 2002, Beszédes Árpád és Ferenc Rudolf Objektumorientált nyelvek és módszertanok