Ugrás a tartalomhoz Lépj a menübe

2.4 Programtervezés

2018.06.07

Az Unified Modeling Language (UML) fogalma, tervezési alapelvei. Az UML diagramtípusainak csoportosítása, felsorolása. Az osztálydiagram szerepe és jelölésrendszere.

 

Az UML fogalma, tervezési alapelvei

Az UML tehát egy nyelv (a szó informatikai értelmében, mint arra rövidesen még kitérünk). A definíciójához használjunk fel hiteles forrást. Az OMG Unified Modeling Language Specification, vers. 1.5 dokumentum 1.1 pontjának (Overview) első két mondata:

“The Unified Modeling Language (UML) is a language for specifying, visualizing, constructing, and documenting the artifacts of software systems, as well as for business modeling and other non-software systems. The UML represents a collection of the best engineering practices that have proven successful in the modeling of large and complex systems.”

A fenti definíció szerint tehát az UML rendszerek modelljeinek vizuális dokumentálására alkalmas eszköz. A „rendszer” lehet szoftver, üzleti folyamatok rendszere, vagy bármi más is. Természetesen számunkra a szoftver alapú rendszerek modelljeinek leírása a legfontosabb. Fontos kulcsszó még a fenti meghatározásban az, hogy komplex rendszerek modellezésére is alkalmas eszközként határozza meg az UML-t.

Az OMG definíciója:

"The Unified Modeling Language (UML) is a graphical language for visualizing, specifying, constructing, and documenting the artifacts of a software-intensive system. The UML offers a standard way to write a system's blueprints, including conceptual things such as business processes and system functions as well as concrete things such as programming language statements, database schemas, and reusable software components."

A fenti definíciókból is kitűnik, hogy az UML egy eszköz (nyelv) a fejlesztés során előállítandó modell egyes elemeinek dokumentálására, de nem módszertan. Önmagában nem ad tanácsokat vagy előírásokat, hogy elemeit mikor, milyen sorrendben, mire érdemes használni. Ebből a szempontból hasonló a programozási nyelvekhez: ha valaki csupán egy nyelv szintaktikáját és szemantikáját tanulja meg, még nem fog tudni működő, és valós problémát megoldó programot írni – ehhez az adott nyelv eszközeinek lehetséges felhasználási módjait is meg kell tanulnia (algoritmusok, adatszerkezetek, programtervezési és programozás technikai fogások stb.).

Az UML tehát számtalan módon felhasználható a szoftverfejlesztési folyamat támogatására, de hogy ez hogyan történjen, a konkrét módszertanok írhatják elő. Annyit azonban megállapíthatunk, hogy szabványosságából és a szakmán belüli ismertségéből adódóan gyakorlatilag minden ma folyó fejlesztési projekt felhasználja, bármilyen fejlesztési módszertant is alkalmaz.

Az UML tervezése során az alkotók rögzítettek néhány alapvető célkitűzést. A továbbiakban vegyük sorra ezeket az alapelveket.

7.2.1. Kifejező vizuális modellező nyelv biztosítása

A fejlesztőknek egy olyan jól használható, kifejező modellező nyelvre van szükségük, amelynek segítségével pontosan le tudják írni a modell egyes elemeit és azok összefüggéseit. Ezek a modell leírások a fejlesztő csapat minden tagja számára ugyanazt kell jelentsék. Az UML egységbe foglal számos olyan modellezési eszközt, amelyek különböző módszertanokban megtalálhatók. A tervezés során az volt a cél, hogy az UML eszközei alkalmazhatók legyenek különböző jellegű és komplexitású rendszerek fejlesztése során is. Ennek érdekében számos elemet definiál, amelyekből egy adott fejlesztés során elég csak a projekt sajátosságainak megfelelőket alkalmazni. Így egyszerre biztosít lehetőséget a nagy bonyolultságú, nagy méretű és az egyszerűbb rendszerek fejlesztésére, és képes alkalmazkodni a különböző jellegű rendszerek (például vállalati információs rendszerek, real-time rendszerek, web alkalmazások stb.) igényeihez.

7.2.2. Lehetőség az alap koncepció bővítésére és specializálására

Az alkalmazásfejlesztéssel szemben támasztott mai követelmények szükségessé teszik, hogy az UML alkalmazkodni tudjon újonnan felmerülő igényekhez, újonnan kifejlesztett technológiákhoz és speciális fejlesztési területekhez. Az UML lehetővé teszi, hogy

  1. egy legtöbb, szokásos jellegű  fejlesztéshez elegendő legyen az alap eszköztár
  2. új elképzelésekkel az alapok módosítása nélkül legyen kiegészíthető (kiterjesztési mechanizmus)
  3. egy adott alkalmazásterület speciális igényei szerint testre szabható legyen

7.2.3. Programozási nyelvtől és módszertantól független legyen

Az UML használható a ma ismert bármelyik programozási nyelvet és módszertant használó fejlesztés során, mert azoktól független absztrakciós szinten fogalmazza meg a rendszer modelljét. Például egy megfelelő részletességű osztálydiagram implementálható Java osztályok halmazaként, vagy akár SQL utasítások sorozataként. Az UML diagramtípusait pedig minden jelenleg ismert módszertan be tudja illeszteni a saját koncepciójába.

7.2.4. Biztosítson formális alapot a modellező nyelv megértéséhez

Az UML egy nyelv a szó informatikai értelmében (mint például a programozási nyelvek.) Ahhoz, hogy UML-t támogató eszközök készülhessenek, precíz (lehetőleg formális) definíciók szükségesek az eszköz készítők számára. Az UML formális definíciója egy metamodell, amit osztály diagramok segítségével definiáltak. Bár ez matematikai értelemben nem teljesen pontos definíció, ezért néhány eleme szóbeli értelmezésre is szorul, a fejlesztőeszközök írói számára elegendő.

Az UML nyelv alkalmazóinak ez a metamodell túlságosan absztrakt, ezért számukra több magyarázatot tartalmazó specifikáció, illetve számos tutoriál áll rendelkezésre.

7.2.5. Támogatja az objektum orientált eszközök fejlesztését

Miután a szakemberek által ismert és használt, szabványos eszköz, UML diagramok készítésére számos program került kifejlesztésre. Ezek közül a legegyszerűbbek csupán diagram rajzolók, de vannak a teljes fejlesztési ciklust lefedni szándékozó fejlesztési környezetet biztosító termékek is.

7.2.6. Az eddigi gyakorlati tapasztalatok ("best practices") integrálása

Az UML tekinthető egy olyan koncepciónak, ami az eddigi fejlesztések tapasztalatait feldolgozva, integrálja az azok során összegyűlt bevált megoldásokat.

UML diagram típusok

Az UML 13 különböző diagramtípusból épül fel. A 2.0 és magasabb verziók a diagramokat két fő csoportba sorolják.

Strukturális diagramok (structural modeling diagrams): a modell statikus architektúrájának definiálására alkalmasak. Azokat az elemeket (és egymás közötti kapcsolatait és függőségeiket) modellezhetjük segítségükkel, amelyekből a rendszer felépül: osztályok, objektumok, interfészek, fizikai komponensek.

Viselkedés diagramok (behavioral modeling diagrams): a modell dinamikus aspektusainak modellezésére használatosak. A modell statikus elemeinek együttműködését, az egyes elemek viselkedését írhatjuk le segítségükkel. Mindig van idő dimenziójuk.

Strukturális diagramok:

  1. osztály diagram (class diagram)
  2. objektum diagram (object diagram)
  3. csomag diagram (package diagram)
  4. összetett struktúra diagram (composit structure diagram) [„montázsdiagram”, „architektúra diagram”]
  5. komponens diagram (component diagram)
  6. telepítési diagram (deployment diagram) [„kihelyezési diagram”]

Viselkedés diagramok:

  1. használati eset diagram (use case diagram)
  2. aktivitás diagram (activity diagram)
  3. állapotgép diagram (state machine diagram) [„állapot diagram” „állapot-átmenet diagram”, „állapotautomata”]
  4. kölcsönhatás diagramok:

A számos diagram típusa áttekintését segíti az alábbi ábra:

7.2. ábra - Az UML digaram típusai

 

dada.jpg

 

Az osztálydiagram

Az osztálydiagram az UML egyik legtöbbet használt diagram típusa.

Ebben az alpontban összefoglaljuk az osztálydiagramok elemeire vonatkozó szabályokat, de nem foglalkozunk az egyes elemek felhasználási módjaival. Az ismertetés nem teljes. Az egyszerűsítés kedvéért és a terjedelmi korlátok miatt néhány – a tapasztalatok szerint ritkábban használt – elemet nem tárgyalunk. Az osztálydiagramnak a modellezés egyes fázisaiban betöltött szerepét a következő fejezetekben tárgyaljuk.

Az osztálydiagram osztályokat és azok kapcsolatait ábrázolja. Az „osztály” fogalma az objektum orientált programozásból már ismerős, ezért fontos hangsúlyozni, hogy az UML osztály fogalma nem teljesen azonos a programozási nyelvekből megismerttel, attól általánosabb értelemben használjuk.

Az UML terminológiájában az osztály a rendszer valamely statikus, strukturális összetevője. Az analízis modell felállítása során az osztály a problématér (alkalmazási szakterület) fogalmait reprezentálja. Ugyanakkor – a programozási nyelvekhez szemléletéhez hasonlóan - tekinthető absztrakt típusként is, amelynek vannak adatokkal leírható tulajdonságai (attribútumai) és viselkedési mintái (operációi). Ez az értelmezése a tervezési és az implementációs modell elkészítése során válik hangsúlyosabbá.

Az UML osztály által modellezett rendszer elemek kiterjedése is eltérő a programozási nyelvek osztályaitól: a modell részletezettségének szintjétől függően akár egy egész alrendszer is lehet, mert határait nem (feltétlenül) implementációs szempontok határozzák meg.

AZ UML osztályait különböző absztrakciós szinteken is megfogalmazhatjuk. Az egyes fejlesztési fázisok különböző megközelítést tesznek szükségessé, ennek megfelelően különböztethetjük meg az alábbi absztrakciós szinteket.

  1. Analízis, elemzés. Ezen a szinten az osztályok az alkalmazási szakterület fogalmait reprezentálják. Az elsődleges feladat, hogy ezeket a fogalmakat és kapcsolataikat értsük meg, ezért számos részlet még hiányozhat. Egy elemzési osztálydiagram elemei közvetlenül még nem lennének implementálhatók.
  2. Tervezés. Mivel a fázis célja az analízis során megismert struktúra technikai megvalósíthatóságának a biztosítása, az elemzési osztályok a tervezés során feltárt további információkkal és implementációs részletekkel bővülnek. Ezek mellett a modellben megjelennek olyan osztályok is, amelyek nem a szakterület fogalmait modellezik, hanem technikai elemei a megoldásnak (a problématér osztályai mellett megjelennek a megoldási tér osztályai is).
  3. Megvalósítás, implementáció. Ezen szinten az osztályok minden olyan részletet tartalmaznak, amelyeknek segítségével egy adott programozási eszköz nyelvi elemei leképezhetők. Ehhez már ismerni kell a célnyelvet, mert nem minden szabályos UML osztály valósítható meg bármely implementációs környezetben (például másként kell megtervezni egy olyan osztályt, amelyet Java osztályra képezünk le, mint amelyet egy adatbázis táblára).

 

A szoftverprojektek életciklusa, a felhasználói és fejlesztői dokumentációk tartalma.

 

?

 

Tesztelési módszerek (feketedobozos és fehérdobozos) és hibakeresési technikák (kiírás, nyomkövetés, változók aktuális értékeinek a vizsgálata, töréspontok, feltételes töréspontok, kivétel fogalma, kivételek elkapása).

 

Az előző részben a statikus tesztelési módszereket vizsgáltuk, ahol a tesztelést a program végrehajtása nélkül, a program szövegének vizsgálatával végeztük. A dinamikus tesztelési módszerek alapelve éppen az, hogy a programot működés közben vizsgáljuk.

Teszteseteket kétféle módon tudunk választani.

  • Egy lehetőség az ún. feketedoboz-módszer, más néven adatvezérelt tesztelés. E módszer alkalmazásakor a tesztelő nem veszi figyelembe a program belső szerkezetét, pontosabban nem azt tekinti elsődleges szempontnak, hanem a teszteseteket a feladat meghatározás alapján választja meg.A cél természetesen a lehető leghatékonyabb tesztelés elvégzése, azaz az összes hiba megtalálása a programban. Ez ugyan elvileg lehetséges, kimerítő bemenet tesztelést kell végrehajtani, a programot ki kell próbálni az összes lehetséges bemenő adatra. Ezzel a módszerrel azonban, mint korábban láttuk, mennyiségi akadályba ütközhetünk.
  • Egy másik lehetőség a fehérdoboz-módszer (logika vezérelt tesztelés). Ebben a módszerben a tesztesetek megválasztásánál lehetőség van a program belső szerkezetének figyelembevételére is. A cél a program minél alaposabb tesztelése, erre jó módszer a kimerítő út tesztelés. Ez azt jelenti, hogy a programban az összes lehetséges utat végigjárjuk, azaz annyi tesztesetet hozunk létre, hogy ezt elérhessük vele. Az a probléma, hogy még viszonylag kis programok esetén is igen nagy lehet a tesztelési utak száma. Gondoljunk a ciklusokra! Sőt ezzel a módszerrel a hiányzó utakat nem lehet felderíteni.

Mivel sem a fehérdoboz-módszerrel, sem a feketedoboz-módszerrel nem lehetséges a kimerítő tesztelés, el kell fogadnunk, hogy nem tudjuk egyetlen program hibamentességét sem szavatolni. A további cél ezek után az összes lehetséges teszteset halmazából a lehető leghatékonyabb teszteset-csoport kiválasztása lehet.

A tesztelés hatékonyságát kétféle jellemző határozza meg: a tesztelés költsége és a felfedett hibák aránya. A leghatékonyabb teszteset-csoport tehát minimális költséggel maximális számú hibát fed fel.

DefinícióPróbának nevezzük a tesztesetek azon halmazát, amellyel a programot teszteléskor kipróbáljuk.

DefinícióIdeális próba az a próba, amellyel a programban szereplő összes hibajelenség felfedezhető.

Definíció: Adott e szinten megbízható próba az a próba, amellyel 1–e valószínűséggel felfedjük az összes hibajelenséget.

A megbízhatóság pontos mérése sem oldható meg általában, így a teszteléskor csak megérzéseinkre alapozhatunk.

 

Hibakeresési eszközök

A hibakeresési eszközök a programozási környezet olyan elemei, amelyek a hiba okának megállapítását, a hiba helyének megkeresését teszik könnyebbé azzal, hogy futás közbeni információt szolgáltatnak a programról.

Egyes eszközök alkalmazásához a programszövegbe tesztelő utasításokat kell elhelyezni, az eszközök alkalmazása után pedig eltávolítani. A gyakori hibakeresésnél azonban sokszor éppen azt kell visszaírni, amit előzőleg eltávolítottunk. Emiatt a jó fordítóprogramok működnek ún. tesztelő üzemmódban, amelyben a tesztelő utasításokat is bele kell fordítani a célprogramba, normál üzemmódban azonban nem. Ilyen esetben a tesztelő utasításokat nem kell kitörölni a programszövegből.

  • Kiírás

A leggyakrabban használt eszköz alkalmas adatkiírások elhelyezése. Kétféle fajtája lehet.

Az egyikben a programszöveg bizonyos helyeire helyezünk el tesztkiírásokat. Ha a futás során arra a pontra érünk, akkor a benne szereplő változókat a program kiírja, majd várakozik a továbbindításra. A másikban a kiírandó változókat rögzítjük, és értékük a futás során mindig látszik/megnézhető a képen. Ez utóbbi gyakran más eszközökkel (pl. töréspont) kombináltan jelenik meg. Egy primitívebb fajtája az assembly programoknál használatos regiszter, illetve memóriakiírás.

  • Nyomkövetés

A nyomkövetés lényege a végrehajtott utasítások követése a programban. Itt tehát futás során az eredményképernyő mellett a programszöveget is látnunk kell. A programszövegből vagy az éppen végrehajtott utasítást látjuk, vagy a teljes programszövegben mutatja egy mutató az aktuális utasítást, vagy pedig algoritmikus struktúrák végrehajtását figyelhetjük meg. A nyomkövetés kiterjedhet a program egészére, de megjelölhetünk programrészeket, és ilyenkor csak ezek végrehajtását kell követni. A programrész lehet a teljes program; egy adott eljárás és mindazok, akiket hív; vagy egy adott eljárás az általa hívott eljárások nélkül. Egyes esetekben pedig mi helyezhetünk el a programszövegben tetszőleges helyre nyomkövetést bekapcsoló, illetve kikapcsoló utasításokat. A nyomkövetés általában sokféle információt adhat a program futásáról: a végrehajtott utasítás mellett kiírhatjuk a képernyőre annak hatását (értékadásnál a változóba elhelyezett értéket, elágazás- vagy ciklusfeltétel kiértékelésénél annak igaz vagy hamis értékét). Egy szűkebb információt adó változatban csak bizonyos típusú utasításokat nyomkövetünk, például az eljárás- és függvényhívásokat.

  • Adatok nyomkövetése

A nyomkövetés egy speciális változatában nem az utasításokat vizsgáljuk, hanem a változókat. Ebben az esetben akkor kapunk a képernyőn üzenetet, ha a kijelölt változó(ka)t valaki használja, illetve módosítja. Az adatok nyomkövetését könnyen megvalósíthatjuk az általunk készített típusok moduljaiban: csupán annyi a teendőnk, hogy a típust kezelő eljárásokat ellássuk megfelelő kiíró utasításokkal.

  • Nyomkövetés a hibától visszafelé

Ez egy olyan nyomkövetési eljárás, amely akkor lép életbe, ha a program futása hibával megszakadt (valamint ha a lépésenkénti végrehajtás során mi magunk kezdeményezzük ezt). Ekkor – a végrehajtásról gyűjtött adatok alapján – elindulunk a programban „visszafelé”, és látjuk mindazt, amit a normális nyomkövetésnél láttunk.

  • Töréspontok elhelyezése

A töréspontok a program olyan utasításai, amelyeknél a végrehajtásnak meg kell állnia, a felhasználó információt szerezhet a program állapotáról, majd folytatódhat a végrehajtás. Egy speciális fajtája az a – végtelen ciklusok felfedésére szolgáló – változat, amikor a töréspontnak kijelölt utasításnak csak adott darabszámszori végrehajtása után kell a futást felfüggeszteni. Leálláskor a felhasználó dönthet a futtatás abbahagyásáról, illetve folytatásáról. Megnézheti változók értékeit, nyomkövetést be- és kikapcsolhat, töréspontokat megszüntethet, illetve újakat definiálhat stb. Sőt némely környezet azt a – nem veszélytelen – lehetőséget is biztosítja, hogy egy-egy változó értékét módosítsuk, és újabb elindítás nélkül lokalizálhassunk esetleges más hibákat is. (Veszélyes, mert elvonhatja a figyelmünket az épp megtalált hibáról, illetve ha nem minden szükséges változót állítottunk megfelelő értékűre, akkor „álhibák” keletkezhetnek, vagy meglévő hibák kendőződhetnek el.)

  • Töréspontok elhelyezése

A töréspontok a program olyan utasításai, amelyeknél a végrehajtásnak meg kell állnia, a felhasználó információt szerezhet a program állapotáról, majd folytatódhat a végrehajtás. Egy speciális fajtája az a – végtelen ciklusok felfedésére szolgáló – változat, amikor a töréspontnak kijelölt utasításnak csak adott darabszámszori végrehajtása után kell a futást felfüggeszteni. Leálláskor a felhasználó dönthet a futtatás abbahagyásáról, illetve folytatásáról. Megnézheti változók értékeit, nyomkövetést be- és kikapcsolhat, töréspontokat megszüntethet, illetve újakat definiálhat stb. Sőt némely környezet azt a – nem veszélytelen – lehetőséget is biztosítja, hogy egy-egy változó értékét módosítsuk, és újabb elindítás nélkül lokalizálhassunk esetleges más hibákat is. (Veszélyes, mert elvonhatja a figyelmünket az épp megtalált hibáról, illetve ha nem minden szükséges változót állítottunk megfelelő értékűre, akkor „álhibák” keletkezhetnek, vagy meglévő hibák kendőződhetnek el.)

  • Lépésenkénti végrehajtás

Ez tulajdonképpen olyan eszköz, amely a program minden utasítására egy töréspontot definiál. A program minden utasításának végrehajtása után lehetőség van a töréspontoknál ismertetett beavatkozásokra.

  • A hiba helyének és okának kijelzése

Fordítóprogramoknak általában vannak olyan lehetőségei, hogy a futás közbeni ellenőrzéseket beépítsék a program kódjába, illetve kihagyják belőle. A kihagyás a már biztosan helyes programnál futási időt csökkentő tényező lehet, emiatt érdemes a kész programokat így lefordítani. A befordított ellenőrzések (pl. túlcsordulás, indextúllépés) viszont elősegítik a hibajelenségek minél korábbi felismerését.

  • Megjegyzés

Itt kell megjegyeznünk, hogy az ilyen hibaüzenetek nem a program majdani felhasználójának szólnak, hanem a programfejlesztőnek. Programfelhasználó a nyelvi környezet hibajelzéseivel nem találkozhat! Arról is tudnunk kell, hogy minden hibafigyelő mechanizmus lassítja a programot, növeli annak méretét, tehát hatékonyság szempontjából kedvezőtlen.

  • Állapotellenőrzés

Ha a programot valamely formális tervezési eszközzel készítettük (levezetés, helyességbizonyítás), akkor rendelkezésünkre állnak a program állapotára vonatkozó állítások. Ilyen állításokat egyébként tetszőleges programhoz is készíthetünk. Az automatikus ellenőrző rendszer feladata annak ellenőrzése, hogy a program megfelelő pontjain ezek az állítások valóban teljesülnek-e vagy sem. Ez az eszköz hasonlít az előzőre, de nemcsak az ún. fatális hibákat jelzi, hanem a futás közbeni teljes állapotot ellenőrzi. Az állapotellenőrzés alkalmas lehet feltételes töréspontok, feltételes kiírásának elhelyezésére a programban.