Informatika gyűjtemény

Egy szinttel feljebb Programtervezés

2004050607080910

NézetNyomtat

Bevezetés

A következő írás a programok tervezésének szükségességéről és egyes fontosabb lépéseiről szól. Természetesen rengeteg különböző tervezési módszer létezik, amelyekről nem fogok szót ejteni, csupán egy adott példán keresztül bemutatom a legegyszerűbb és legfontosabb tervezési paradigmákat, amik a fejlesztést kísérik.

Miért szükséges a tervezés?

Mielőtt egy program írásába belekezdenénk, különösen fontos, hogy pontosan megtervezzük előre a programunk alapvető szerkezét. Ha nagyobb szoftvert írunk mindenféle tervezés nélkül, legyen az bármennyire is ésszerűen megvalósítva, az idő előrehaladtával a kódunkra egyre inkább az kuszaság és az instabilitás, a fejlesztésre pedig a lassú és nehézkes haladás lesz jellemző.
Alapos tervezés után csak implementálni kell az egyes függvényeket, és így szinte teljes mértékben kizárhatjuk az fejlesztés során fellépő, utólagos módosításokkal járó komoly problémákat. Persze gyakran előfordul, hogy egy programot annak befejezése után kell módosítani (pl. felhasználói kérésre). Ilyenkor nincs mit tenni, sajnos bele kell piszkálnunk a forráskódba.
A tervezés során arra is fel kell tehát készülni, hogy a program utólagos fejlesztése során valamennyi apróbb részlet könnyen módosítható legyen. Ezt úgy érhetjük el, hogy a programot önálló modulokra bontjuk szét, és az egyes modulok között laza kapcsolatot tartunk fenn. Egy jól megtervezett szoftver esetén egy módosítás csak nagyon kis, vagy semmilyen hatással nem lesz a program többi részére.

A fejlesztés lépései

Specifikációanalízis

A legelső dolog, amit egy program fejlesztésekor meg kell tennünk, hogy alaposan átrágjuk magunkat a specifikáción. Ez nem egyenlő azzal, hogy nagyjából megértettük a feladatot. Sok időt lehet azzal tölteni, hogy a specifikáció szövegét átalakítjuk formális leírássá, ezzel mi most nem foglalkozunk.
A következő példában a Parcellák nevű feladatot fogjuk lépésről lépésre elemezni, illetve megtervezni az egyes részeit. A specifikáció pontos szövege itt megtalálható. A szöveget elemezve a következő alapvető állításokat kaphatjuk meg
  • A földműves parcellákra osztja a termőföldjét.
  • Egyszerre egy parcellát oszt fel 4 (vagy kevesebb) részre.
  • A termőföld és a parcellák téglalap alakúak.
  • A parcellák közé sétányokat helyez el.
  • Minden felosztáshoz egy metszéspont tartozik, amely jellemzi a felosztást.
Lássuk, mik az egyes alanyai és tárgyai a mondatoknak:
földműves, parcella, termőföld, sétány, felosztás, metszéspont
Ezekből a főnevekből, mint önálló egységekből kell nekünk összeállítani a programunk különböző moduljait (objektum-orientált nyelvekben osztályait). Ha szükséges, további modulokat felvehetünk még ezeken kívül is.
Nézzük az első szót: földműves. A földművesünk viszi véghez a földosztás műveleteit. Ő ismeri a termőföldet, illetve az azokon lévő parcellákat. Modulnak abszolút megfelel, felvehetjük.
Második szó: parcella. Őróla tudjuk, hogy téglalap alakú, tehát egy koordináta és két oldalhossz jellemzi. Tudnunk kell a területének nagyságát, és tudnunk kell felosztani. Őrá is szükségünk lesz.
Harmadik szó: termőföld. Nagyon hasonló tulajdonságokkal bír, mint a parcella. Tulajdonképpen ő a legnagyobb parcella, amit a későbbiekben felosztunk. Nem szükséges tehát megkülönböztetnünk a két fogalmat, így ennek nem kell külön, önálló modul.
Negyedik szó: sétány. A feladat megoldása szempontjából nem kell ismernünk a sétányokat, hiszen nem szükségesek ahhoz, hogy egy parcellát felosszunk, vagy megkeressük a legnagyobb méretű parcellát. Így tehát úgy döntünk, nem vesszük fel modulnak. De vigyázat! A program többi részét úgy kell megírni, hogyha időközben mégis szükséges volna kilistázni az egyes sétányokat, akkor utólag ezt könnyen megtehessük.
Ötödik szó: felosztás. Valójában ez nem egy önálló objektum, hanem egy művelet. (Igéből képzett főnév.) Így ő is kimaradhat. (Megj.: ha mégis eltárolandó adatként kellene kezelnünk, egy parcella és egy metszéspont jellemezné.)
Utolsó szó: metszéspont. Ő egy olyan koordináta, amely a felosztás művelethez tartozik. Máshol nincs rá szükség. De emlékezzünk csak: a parcelláknál is szükség volt koordinátákra. (Megj.: Valójában egy parcellát egy koordináta-párral tudunk jellemezni. Pl. a téglalap bal felső és jobb alsó sarka.) Így tehát elég felvennünk egy koordináta modult, amely jellemzi a termőföld egy adott egységnyi pontját.
Megkaptuk tehát a fő modulokat, amelyekre szükségünk lesz. Hogy ezek pontosan milyen adatszerkezeteket és műveleteket fognak tartalmazni, az csak a konkrétabb tervezés során derül ki.

Részletes tervezés

A következőkben UML osztálydiagramokat használtam a C# nyelv szintaktikája szerint, hogy könnyebben el lehessen képzelni az egyes modulok (osztályok) szerkezeteit. Ha esetleg nem teljesen érthetőek, nyugodtan át lehet ugrani ezeket a részeket.
Itt már pontosan végig kell gondolnunk, hogy milyen algoritmusokat, eljárásokat, függvényeket, adatforrásokat (pl. fájl, adatbázis), illetve felhasználói elemeket fogunk használni.
Példánk előzetes feladatkidolgozása itt olvasható.

Felülről lefelé tervezés
A feladat szerint szükségünk lesz fájlból olvasó, illetve fájlba író műveletekre, amik az adatkezelést irányítják. Legyenek ezek a műveletek a programunk fő moduljában, amik a program indításakor, illetve annak végén meghívódnak. A beolvasás alatt a Földműves modul (vagy egy abból példányosított objektum) fogja elvégezni a földosztás műveleteit, rögtön az egyes metszéspontok beolvasását követően.
A legfőbb modul ezennel kész is, már csak az egyes almodulok maradtak hátra.
Amikor a központi modulból haladunk lefelé egyre részletesebb képet kapva a programunkról, azt felülről lefelé tervezésnek hívják.

Alulról felfelé tervezés
A fenti eljárás fordítottja: elindulunk egy olyan független modulból, aki senki másra nem támaszkodik, és pontosan meghatározva az ő szerepét részletesen kidolgozzuk. (Itt szóba jöhet akár az implementálás, majd az ezt követő tesztelés lehetősége is, de ezzel lehetőleg ritkán éljünk.)
Példánkban a legprimitívebb modul a Koordináta volt, melyet az egyszerűség kedvéért hívjunk röviden Mező-nek. A mező nem ismeri az őt tartalmazó földet, csupán egy X, Y számpárból, mint koordinátából áll. Ezeket tudunk kell lekérdezni, illetve beállítani. Egyéb művelet nem tartozik a modulhoz.
A következő kirészletezendő modul a Parcella lesz, mivel ő csak a Mező modult ismeri és használja. Egy parcellát sokféleképpen tárolhatunk. Ha ismerjük a bal felső sarokpontját, valamint oldalainak hosszát, megkaphatjuk azt a téglalapot, ami a parcellát jellemzi. Mivel a parcellák felosztásánál nehézkes lenne az oldalhosszokkal számolni, így a következő megoldás mellett maradunk: a parcellát megtestesítő téglalapot egy koordinátapárral írjuk le. A téglalap bal felső és jobb alsó sarokpontja pontosan meghatározza a parcella helyzetét és méreteit.
Egy parcelláról a további jellemzőket érdemes ismerni:
  • bal szélének pozíciója
  • jobb szélének pozíciója
  • felső szélének pozíciója
  • alsó szélének pozíciója
  • parcella szélessége
  • parcella hosszúsága
  • parcella területe
Mivel ezek a parcella adataiból könnyen kiolvashatók, nem kell módosítanunk az adatszerkezeten.
Nézzük tovább, milyen műveletekre lesz még szükségünk a parcelláknál. Egyedül a felosztás művelet kapcsolható hozzá, de azt tudjuk, hogy parcellát felosztani nem a parcella, hanem sokkal inkább az őt tartalmazó Földműves felelőssége.
Jó lenne viszont tudni, hogy egy adott mező benne van-e a parcellában (szükséges a felosztási algoritmusunkhoz). Így az adatlekérdező és -módosító műveleteken kívül feldeszünk egy Tartalmaz(Mezo) függvényt is, amely egy igaz/hamis értékkel tér vissza, ha a mező a parcella része.
Nézzük tovább: a következő modulunk a Földműves. Ő felel az algoritmusunk lebonyolításáért. Tudjuk, hogy az ő tulajdonában áll a földterület és annak összes parcellája. Pontosabban egy parcellahalmaz, amik tetszőleges sorrendben tartalmazhatják az egyes parcellákat. És most döbbenünk rá, hogy jól jönne egy külön parcellahalmaz modul, ami a parcellák tárolásáért felelős. A specifikációban nem szerepelt, így csak implementációs szinten derül ki, hogy szükség volna rá. Kivételesen most egy új segédmodult fogunk felvenni.
Parcellahalmaz: egy segédmodul, amely segít rendszerezni a parcellákat. Mivel nem szeretnénk dinamikusan tárolni az adatokat (lehetne, de ez meghaladja feladatunk nehézségi szintjét), így a halmazt jellemezni fogja egy maximális méret, úgynevezett kapacitás. (Megj.: objektum-orientált nyelveknél célszerű a kapacitást az halmaz objektum konstruktorában megadni.)
A halmazhoz alapvető adatok tartoznak: egy parcella objektumokból álló tömb és a halmaz mérete. (Megj.: strukturális, azaz nem objektum-orientált nyelvekben is léteznek kezdetleges objektumok. Pascalban a recordot, C-ben pedig a structot használják erre a célra.)
A halmazhoz csak a legfontosabb műveleteket rendeljük: elem hozzáadása, törlése, illetve kiolvasása adott index szerint, illetve a halmaz méretének lekérdezése. Más műveletet a halmazunknak nem kell tudnia.
Már csak egy utolsó, a Földműves modul maradt hátra. Neki ismernie kell a saját telkét (ami a legnagyobb parcella), továbbá annak egyes részparcelláit, vagyis az előbbiek szerinti parcellahalmazt. Ezek szerepelnek majd az ő adatszerkezetében. Mint fentebb már volt róla szó, a Földműves felelőssége a parcellák részparcellákra osztása. Így ez a művelet az ő moduljában fog szerepelni. Pontosabban: a kapott koordináta szerinti parcellát kiválasztja, azt törli a halmazból, majd megfelelő algoritmus szerint több részre osztja, ezeket a halmazhoz adja.
Egyetlen kérdés maradt hátra, mégpedig a legfontosabb. Kihez tartozik annak megválaszolása, hogy melyik a legnagyobb területű parcella? A válasz nem triviális, hiszen ketten is ismerik az összes parcellát: egyrészt a Parcellahalmaz tárolja a parcellákat, másrészt a Földműves ismeri a Parcellahalmaz összes elemét.
Logikailag nem illik egy halmaz modul struktúrájába olyan speciális művelet, mint a legnagyobb méretű parcella megkeresése (persze lehetne, de ez már a programozó döntésén múlik). Továbbá a főmodul, amely a fájlkezelésért felelős, csak a Földművest ismerheti, a felosztott parcellákat nem. Így abban maradunk, hogy a földműves felelőssége lesz a feladatban feltett kérdés megválaszolása.

Ellenőrzés

Most már készen állunk arra, hogy az előbbiekben felsorolt modulokat összekapcsoljuk egy nagy rendszerré, és még egyszer utoljára (immáron minden szükséges ismerettel) ellenőrizzük annak helyességét.

Implementálás és tesztelés

Miután elkészültünk a tervvel, három dolog maradt hátra: a dokumentálás, az implementálás (hétköznapi nevén kódolás) és a program tesztelése. A dokumentálással itt most nem foglalkozunk, az utóbbi kettőt pedig mindenki a legjobb tudása alapján végezze el.

Végszó

A fentebb leírtak a szerző egyetemi tanulmányai és saját szoftverfejlesztési tapasztalatai alapján készültek. Remélhetőleg ez az írás sokaknak fog tudni segíteni a nagyobb programok elkészítésénél.