kategória | ||||||||||
|
||||||||||
|
||
A szoftver krízis.
A strukturált és az objektum-orientált tervezés jellemzői.
A legelső elektronikus számítógépek utasításait, programját kapcsolótáblákon állították be, de már az I. generációs gépek korában megjelentek azok a módszerek, amelyekkel off-line eljárással lyukszalagra vagy lyukkártyára rögzítették a gépi utasításokat, a 8-as vagy a 16-os számrendszer jeleivel rövidített formában. Ezekből a kódokból a bináris változatot "egyszerű" konvertáló áramkörökkel hozták létre. Ezt a programozási technikát nevezzük ma gépi kódú programozásnak. A gépi kódú programok tervezés 424j99e e nem volt könnyű feladat. A folyamatábrák, mint programtervezési eszközök ebből a korból származnak. Jelentőségük a programtervezésben ma már meglehetősen korlátozott, de a bonyolultabb elágazások, ciklusok szemléltetésére, illetve a következőkben tárgyalt assembly nyelvi környezetben még ma is használjuk őket.
A programozási nyelvek fejlődésének következő állomása az Assembly-nek nevezett nyelvi struktúra volt. Ez az alábbi könnyítéseket jelentette a gépi kóddal szemben:
- Az utasítások kódjait azok angol nevének rövidített alakjaival helyettesítjük. Ezeket emlékeztető kódoknak, idegen szóval mnemoikoknak nevezzük (pl. az összeadás utasítás 04 gépi kódja helyett az írhatjuk, hogy ADD).
- Az operandusok tárbeli címeinek oktális vagy hexadecimális alakja helyett valamilyen értelmes vagy értelmezhető karaktersorozatokat, úgynevezett szimbólumokat használhatunk (pl. ADD szám1, szám2).
- A számokat a megszokott tízes számrendszerbeli alakjukban írhatjuk a programban.
- Az utasítások egy-egy sorozatát összefoghatjuk és egyetlen névvel hivatkozhatunk rá. Ezeket makroutasításoknak hívjuk.
Az assembly szintű programnyelvek hasonlóan a gépi kódú programokhoz, a különböző utasításkészletű (processzorú) gépeken különbözőek, ezért ezeket a nyelveket gépre orientált nyelveknek nevezzük.
Az assembly-ben megírt programkódot már nem lehetett a processzor által elfogadott kódúvá alakítani áramkörök segítségével. Készítettek viszont olyan programokat, amelyek ezt a konverziót elvégezték. Ezeket assemblereknek, vagy assembly fordítóprogramoknak nevezték
A géptől független programozási nyelvek (magas szintű nyelvek) elmélete a digitális számítógépek megjelenésekor már igen jelentős eredményeket mondhatott magáénak. Turing, Church, Neumann nevét említhetjük meg ezzel kapcsolatban. A 60-as években az új programozási nyelvek száma exponenciális növekedésnek indult, de ezek közül csak kevés vált fontossá új fogalmak vagy az alkalmazások szempontjából.
Az első alkalmazható magas szintű nyelv - amihez már fordítóprogram, a compiler is elkészült- a FORTRAN az 1950-es évek közepére az IBM támogatásával jött létre. Elnevezése a FORmula TRANslation -képlet átalakító- rövidítéséből származik. Elsősorban matematikai-műszaki alkalmazásokra szánták. Különböző, egyre fejlettebb verzióit egészen a 70-es évekig széleskörűen használták.
Az ALGOL 60 1963-ban jelent meg, ez elméletileg sokkal jobban kidolgozott nyelv volt, mint a FORTRAN. Az ALGOL 60-ban bevezetett fogalmak nagy része szinte minden későbbi programnyelvben megmaradt, például a blokk, az eljárás, stb. Annak ellenére, hogy az algoritmuselmélettel kapcsolatos publikációk szinte kizárólag ALGOL-ban jelentek meg, ezt a nyelvet konkrét alkalmazásokban csak nagyon ritkán használták fel. Ennek oka talán az volt, hogy I/O utasításokkal nem rendelkezett.
1959-ben az USA hadügyminisztériumának szorgalmazására alkották meg a COBOL-t, a név a COmmon Business Oriented Language - közös üzleti célú nyelv -ből származik. Kidolgozásakor elsősorban adatfeldolgozási szempontokat tartottak szem előtt. A COBOL számos új fogalmat vezetett be. Ezek egyike a géptől független adatleíró rész ötlete, ami később az adatbázisrendszerek fogalmához vezetett. A nyelv az angolt olyan mértékben használta, hogy a programot szinte olvasni lehessen.
A 60-as évek közepén az IBM egy nagyszabású nyelvtervezésbe kezdett a 360-as gépeire. Ez az új nyelv a PL/I (Programming Language One) azzal a szándékkal született meg, hogy a FORTRAN, ALGOL és COBOL lehetőségeit egyesítse. Az univerzális számítógépek univerzális nyelvének szánták. Két alapvető, új fogalmat vezettek be benne:
- a kivételkezelést, ami lehetővé tette, hogy a programozó szokatlan események bekövetkezéséhez utasításokat rendeljen (on endfile.., on error...., stb.),
- a párhuzamosan végrehajtható feladatok (multitasking) kezelésének lehetőségét.
A PL/I számos kedvező tulajdonsága ellenére sem terjedt el olyan mértékben, ahogy azt várták. Ezt elsősorban az okozta, hogy a szoftverkrízis egyre inkább érzékelhető problémaköre nem viselte el a PL/I-ben programozók viszonylag hosszú kiképzési idejét.
A gépi kódú programozás korában a programokkal szemben támasztott legfontosabb követelmény a tárral való takarékosság és a futási idő minimalizálása volt. Ezt az akkori hardver fejlettségi szintjét tekintve nem is kifogásolhatjuk. A processzorok sebességének és az operatív tárak kapacitásának növekedésével azonban egyre nagyobb és egyre bonyolultabb programok születtek. Ezek tervezése, illetve a helyes működés bizonyítása egyre több gondot okozott. A rendszerek kidolgozása késett (néha éveket is), a költség sokkal több lett, mint ahogy azt eredetileg tervezték, a kifejlesztett rendszer pedig megbízhatatlan volt, nehéz volt karbantartani és nem volt olyan hatékony, amint azt el lehetett volna várni. Nyilvánvalóvá vált, hogy a programok méretének növekedésével, a programszerkezetekkel szemben támasztott igényeket át kell értékelni. Amint Dahl a "Strukturált programozásról" című dolgozatában írta:
"Arra a következtetésre jutottam, hogy legsürgősebben véget kell vetni annak a szemléletnek, amely szerint a programozásban a legfontosabb feladat a ráfordítás/ teljesítmény viszony optimalizálása. Fel kell ismernünk, hogy a programozás már ma is sokkal inkább a bonyolultság megszervezésének, a nagy méretekkel járó hatalmas káosz lehető leghatékonyabb elkerülésének a művészete."
A strukturált programozás kényszere
A növekvő szoftverkrízis és a programhelyesség bizonyítása iránti igény egy új (európai) nyelvhez, a PASCAL-hoz vezetett. Ez a korábbiaknál sokkal hatékonyabbnak bizonyult ( természetesen akadnak hibái is, pl. a dinamikus tömbindex-határok, a kivételkezelés vagy a párhuzamos, konkurens eljárások hiánya), amit elsősorban az új tervezési módszerek erős támogatásának köszönhetett. Ezeket a módszereket összefoglaló néven strukturált programozásnak, illetve tervezésnek nevezzük Ezek részletesebben a következőket jelentik:
A tervezés első szakaszában az algoritmust részműveletek sorozatára bontjuk. Például, ha a ti-vel egy bizonyos, meghatározott résztevékenységet jelöltünk, akkor a t1 t2,t3,..,tn szekvenciális sorozat végrehajtása egyben a program végrehajtását is jelenti. Ezeket a hagyományos folyamatábra szimbólumokkal is ábrázolhatjuk:
Ha a résztevékenységek az adott programnyelven közvetlenül kódolhatók, vagy ismert alapalgoritmusokkal helyettesíthetők, akkor a tervezést ezen a ponton be is fejezhetjük. A ti-ket ilyenkor elemi lépéseknek hívjuk. Legtöbbször azonban nem ilyen egyszerű a helyzet. Először is nem minden program építhető fel csupán szekvenciális elemi lépéssorozatokból. Előfordulhat, hogy az egyes tevékenységek végrehajtása bizonyos feltételektől függ. Ezt tisztán a szekvenciális sorozattal nem lehet leírni. A feltételektől függő végrehajtás az
if feltétel then t1;
if feltétel then t1 else t2;
case szelektor of sl :t1; s2:t2;... sn:tn [else tn+1] end
elemi döntésekkel írható le a PASCAL-ban. Az alábbiakban megadtuk a megfelelő folyamatábra szimbólumokat is, de fontos hangsúlyozni, hogy a strukturált tervezésben ezeket önálló egységeknek kell tekinteni, tehát nem bonthatók fel tovább utasításokra.
Könnyen beláthatjuk, hogy a második if és a case utasítás helyettesíthető az első if utasítással azonos szerkezetű elágazásokkal és szekvenciális tevékenységek sorozataival. Sőt bebizonyítható, hogy ez minden programra igaz.
A többször végrehajtandó programrészeket a ciklusutasítások segítségével írhatjuk le. A PASCAL-ban ciklusszervezésre az alábbi utasításokat használhatjuk:
a.) for ciklusváltozó := kezdőérték to végérték do t;
b.) for ciklusváltozó := kezdőérték downto végérték t;
c.) while feltétel do t;
d.) repeat t1,t2,..tn until feltétel,
ahol a t és a t1,..,tn a ciklusmag, vagyis azt a tevékenységet jelöli, amit a paraméterektől függően esetleg többször is végre kell hajtani. Természetesen a tervezéskor megadhatjuk a fenti utasítások mindegyikét magyarul is. Az ilyen tervet "mondatszerű" programtervnek hívták. Emellett számos más grafikus tervezési módszert is kitaláltak, de egyik sem lett olyan népszerű és sikeres, mint a korábbi, az assembly szintű tervezésben bevezetett folyamatábra-rendszer.
Ha a tervezés során a forrásnyelvű kódhoz lépésenkénti finomítással jutunk el és mindenütt csak a fenti sémákat engedjük meg, vagyis a szekvenciális tevékenységek sorozatát, a feltételtől függő elágazásokat és a ciklusokat, akkor az ilyen programtervet strukturáltnak mondhatjuk. Ez persze nem azt jelenti, hogy az algoritmust a fenti folyamatábra szintig kell lebontani, hiszen ehhez az elemi tevékenységeket a nyelv utasításainál kisebb egységekkel kellene megadni, holott a programterv végső alakja a forrásnyelven kódolt program.
A jól strukturált programok megértését, illetve a helyes működésük bizonyítását a
lépésenkénti elemzés,
a teljes indukció és
az absztrakció
módszereinek alkalmazásával kielégítően elvégezhetjük. A lépésenkénti elemzéssel a szekvenciális tevékenységek és az elágazások jól vizsgálhatók, a teljes indukció pedig a ciklusok elemzésének eszköze. Az absztrakció például abban lehet segítségünkre, hogy a programok által kezelt objektumokat egzakt, többnyire matematikai fogalmakkal helyettesíthessük.
Az, hogy a programtervet bizonyos mértékig meghatározza a feladat szintjén megfogalmazott algoritmus, nem szorul különösebb magyarázatra. Talán kevésbé ilyen nyilvánvaló, hogy a program szerkezetére a feldolgozandó adatok szerkezete is igen jelentős hatással lehet. A PASCAL például egy olyan programnyelv, ami rendkívül sokféle és rugalmas adatszerkezettel rendelkezik, így nagyon jól strukturált programokat készíthetünk ezen a nyelven, ha helyesen választjuk meg az adattípusokat. Egy konkrét feladat megoldására persze nagyon sok jó megoldás készíthető. A programozó felelőssége az, hogy jól használja ki az adott programnyelv által nyújtott lehetőségeket úgy az algoritmusok, mint az adatstrukturák megválasztásakor. A jól strukturált programok az alábbi előnyöket nyújtják:
- Az algoritmus könnyen érthető lesz a forráskódból, működését nyomon lehet követni.
- A program moduláris szerkezetű lesz, ami megkönnyíti a lépésenkénti elemzést és az egyes tevékenységek önálló tervezését és kódolását.
A számítógép rendszerek nagyméretű, nagyon összetett és nagyfokú párhuzamosságot tartalmazó alkalmazásainak elősegítésére (ismét az USA Hadügyminisztériuma támogatásával) 1980-ra egy a PASCAL-on alapuló nyelvet hoztak létre, az ADA-t. Megalkotásakor úgy tűnt, a jövő nagy számítógépeinek nyelvét hozták létre. A mikrogépek viharos térhódítása azonban minden jóslatot felborított. Ma már biztosra vehető, hogy az ADA a strukturált programtervezés korának egyik utolsó, nagygépes eljárás-orientált nyelve maradt.
Az eddig említett programnyelveket a számítógépes alkalmazásokhoz fejlesztették ki, az operációs rendszerek, a magas szintű programnyelvek fordítóprogramjai, bizonyos szerviz-programok fejlesztéséhez nagyon sokáig még az assembly-ket használták. Ez érthető is, hiszen ezeknél a szoftvereknél a hardver hatékony kihasználása sokkal fontosabb lehet, mint az alkalmazások esetében. A közepes kategóriájú számítógépek operációs rendszeréhez, a UNIX-hoz fejlesztették ki a C nyelvet. Ez egyesíti magában a magasszintű nyelvek szerkezeti tulajdonságait, támogatja a strukturált és ma már az objektum-orientált programfejlesztést is, illetve az assembly nyelvekhez hasonló szinten lehetővé teszi a hardverhez való közvetlen hozzáférést. A C a közepes kategóriájú gépeken általánosan használt, de ma úgy tűnik, hogy népszerűsége a mikrogépek körében is egyre növekszik. Legújabb, már OOP-s változataival olyan szoftver rendszereket hoztak létre, mint a WINDOWS vagy a NOVELL.
Egy másik területen, a gép és a felhasználó közötti interaktív kapcsolat kezelésében a magyar származású John Kemeny által kifejlesztett BASIC-et (Beginners All-purpose Symbolic Instruction Code- kezdők általános célú szimbolikus utasítás kódja) említhetjük meg. Ez a nyelv nem fordítóként, hanem értelmezőként (interpreter) lett bevezetve és a negyedik generációs gépeken nem várt karriert futott be. Eredeti változatai nem tartoztak az elméletileg jól megkonstruált nyelvek közé, egyik nyilvánvaló hibájuk a strukturált programtervezés támogatásának hiánya volt. Javított, már objektum-orientált változatai azonban szinte mindenütt előfordulnak, ahol interaktivitásra van szükség.
A programozási nyelvekkel, illetve a programozási technikával szembeni újabb kihívást jelentettek a tömegtárolók elterjedésével megjelenő óriási adathalmazok, az adatbázisok. Olyan új algoritmusokra volt szükség, amelyek kielégítik például az alábbi követelményeket:
1. több, egymással összefüggő adatfájlon átvezetett adatmanipulációk,
2. az állományokhoz való párhuzamos hozzáférés lehetősége,
3. az adtatok védelme illetéktelen felhasználóktól és véletlen hibáktól,
4. könnyen tanulható kezelőfelület biztosítása a felhasználónak.
Ezeket az új programrendszereket adatbázis-kezelő rendszereknek nevezték el. Az adatbázisok szervezési módja kétféle adatmodellt használ, a hálóst és a relációst. Ezek közül számítógépen az utóbbi kezelése bizonyult sikeresebbnek. A relációs adatbázis-kezelő rendszerek már a számítógépek III. generációjának végén, IV. generációjának elején megjelentek, de igazán széleskörűen csak a IV. generációs mikrogépeken terjedtek el. Az első relációs adatbázis-kezelő mikrogépes nyelv a dBase interpreter volt. A FOXBASE tulajdonképpen a dBase javított változatának tekinthető, de későbbi verziói, pl. a FOXPRO, már igen jelentős eszközöket tartalmaznak, elsősorban a felhasználói kapcsolatok tervezésében. Az interpreteres változatok mellett fordítókat is készítettek, ilyen például a CLIPPER, a dBase-hez hasonló adottságokkal.
A relációs modell lehetővé tette egy olyan adatmanipulációs nyelv elkészítését, amely az adatszerkezetek megadását, az adatkezelést és az adatbiztonságot matematikailag megalapozott, szabványos eszközrendszerrel oldja meg. Ez a nyelv a SEQUEL (Structured Query Language), az IBM terméke (1976). A nevét később SQL-re változtatták. Az SQL-t ma már minden adatbáziskezelő rendszer tartalmazza, illetve amelyek nem, azok egyszerűen kihaltak.
A miroszámítógépek fejlődésével és robbanásszerű elterjedésével az operációs rendszerek fejlesztői nem tudtak lépést tartani. Az alkalmazások fejlesztőire hárult az a feladat, hogy egy-egy, még erősen hardver-orientált operációs rendszerre építve egy nagyobb feladatcsoport igényeinek megfelelő, könnyen kezelhető alrendszereket hozzanak létre. Ezeket a szoftvereket összefoglaló néven integrált rendszereknek nevezzük. A hardver gyors fejlődése és a strukturált tervezési módszerek lehetővé tették az egyre bonyolultabb programrendszerek létrehozását. Ennek következtében a számítógépeket olyan területeken kezdték használni, amit korábban szinte el sem lehetett képzelni. A teljesség igénye nélkül csak néhány ilyen alrendszert említünk meg.
A DTP (DeskTop Publishing) a szövegkészítés és feldolgozás integrált rendszereinek gyűjtőneve. Elterjedt képviselője pl. a PageMaker és a Corell Draw. A CAD (Computer Aided Design) a műszaki tervezést, a CAM (Computer Aided Manufacturing) a gyártási folyamatok automatizálását segítő alrendszer. Megjelentek az első próbálkozások az oktatás és tanulás számítógépes segítéséhez, a CAI (Computer Aided Instruction) és a CALL (Computer Aided Language Learning). Ma már az integrált rendszerek olyan tömegével találkozik a felhasználó, amit nem lehet egykönnyen rendszerezni. Azt azonban szinte mindegyikről elmondhatjuk, beleértve a legkorábban megjelenteket is, hogy nem lezárt, hanem folyamatosan, néha nagyon gyorsan fejlődő rendszerek.
Objektum-orientált programozás és tervezés (OOP, OOD)
A mikrok elterjedésével csakhamar kiderült, hogy a szoftver krízist nem lehet leszűkíteni a hardver és az alkalmazói szoftver illesztési problémáira. A felhasználói környezet szerepének növekedése oda vezetett, hogy a strukturált programtervezést támogató nyelvek, mint például a PASCAL vagy a C nem bizonyultak elég hatékonynak az új feladatokhoz. Az új programtervezési módszer, amitől a felhasználói interfésszel kapcsolatos gondok megoldását remélhetjük, az objektum-orientált programozás. Alapjait szintén egy a 60-as években megjelent programnyelvben, a SMALLTALK -ban fektették le. Ma már a hagyományos nyelvek újabb változataiba átvették az OOP alapelveit , amelyeket az alábbiakban foglalhatjuk össze:
Az objektumtípus (osztály) a valóság egy modellje, amiben a valóságos objektumtípus (valamilyen létező egyed, pl. ember, vagy absztrakt fogalom, pl. mátrix, négyszög, stb.) adatmodelljét és az ezen manipuláló függvényeket és/vagy eljárásokat, metódusokat adhatjuk meg. A metódusok az objektumok viselkedését írják le.
Objektumtípus=adatmodell+metódusok.
Az objektumok egy objektumtípushoz tartozó változók. Ezek zárt egységek, adataikat csak saját típusuk metódusai ismerik, illetve változtathatják meg. Az objektum környezete, ha ismeri az objektum metódusait, kérheti azok végrehajtását. Ezt a kérést üzenetnek nevezzük.
Egy objektumtípusból további objektumtípusok származtathatók úgy, hogy vagy az adatmodell, vagy a metódusok egy részét megváltoztatjuk vagy kibővítjük. Ez az új típus ismeri az ősének az összes adattípusát és metódusát.
Ha a leszármaztatott objektumtípusban az ős típus adattípusát vagy metódusát újra, ugyanolyan névvel, de más tartalommal adjuk meg, akkor a leszármaztatott objektum ugyanarra az üzenetre másképpen viselkedhet.
Az objektum orientált tervezés és programozás segítségével sokkal nagyobb és bonyolultabb feladatok programjai készíthetők el gyorsan és megbízhatóan, mint a strukturált módszerekkel. Tulajdonképpen a grafikus felhasználói felület (GUI) igénye indukálta ezt a felismerést, de ma már nincs olyan területe a szoftverfejlesztésnek, ahol ne törekednének az alkalmazására. Az említett előnyöknek természetesen ára van, mégpedig a hardver rosszabb kihasználása
Az OOP elterjedése a szoftverfejlesztésben oda vezetett, hogy a hagyományos programozási technikák helyett új grafikus fejlesztő eszközök jelentek meg. Ezek a formális nyelvek szintaxisának ismerete helyett vizuális manipulációkból képesek helyes magas szintű kódot generálni. Ez azt jelenti, hogy a programozás "művészete" szakmai szintről felhasználói szintre csökkenthető. A hagyományos nyelvek "visual" változatai már megjelentek, de más területeken is gyors, használható eredmények várhatók. Pédaként megemlíthetjük az SQL-t és a HTML-t is.
Azt hihetnénk, hogy a hagyományos programozási technikákra ezek után már nem sokáig lesz szükség. Sajnos, ez nem igaz. A számítógépekkel való emberi kommunikáció továbbra is a formális nyelvekre épül. A negyedik generációs eszközök fejlesztése, megbízható használata nem képzelhető el nélkülük.
Az Internetes alkalmazások iránti fokozott igény következménye a JAVA programnyelv, a SUN cég fejlesztése. Természetesen támogatja, sőt egyenesen kikényszeríti az objektum-orientált fejlesztési technikát. Hasonlít a C++-hoz, de könnyebben tanulható és kifejezetten "elosztott" környezetre tervezték.. Ez azt jelenti, hogy a futó program egyes részei, illetve az adatai nem kell, hogy ugyanazon a gépen legyenek. Ahogy a SUN-szlogen mondja: " a hálózat a számítógép". A "hordozható" programokat készít, azaz környezettől, operációs rendszerektől függetlenül használható. Ezt úgy oldják meg, hogy a különböző operációs rendszerekre telepítenek egy JAVA "vituális gépet" (JAVA plattform), ami a fordítóprogram outputját értelmezni fogja. Ezt természetesen operációs rendszerenként külön-külön kell elkészíteni. A fordító egységes, úgynevezett "bájt kódot" készít, amit virtuális gép fog értelmezni és végrehajtani. Emellett a JAVA virtuális gép opcionálisan végrehajtható formára is képes fordítani a bájt kódot, a gyorsabb futás érdekében.
A JAVA applet-ek a WEB-lapokba építhető kis alkalmazások vagy modulok. A JAVA scrip-tek viszont nem tekinthetők igazi JAVA alkalmazásoknak. A script-eket a NETSCAPE cég vezette be, és bár JAVA formátumban írhatók, más értelmezőt igényelnek, nem "hordozhatók" és nem fordíthatók bájt kódra.
Egyéb "egzotikus" nyelvek
A 60-as években keletkezett nyelvek között megjelentek olyanok is, amelyek nem a Neumanni architektúrák kiszolgálását célozzák. Az utasításcentrikus, úgynevezett imperatív nyelvektől megkülönböztetve, ezeket applikatív nyelveknek nevezzük. Egyik legfontosabb képviselőjük a LISP, amit máig is alapnyelvnek tekinthetünk ezen a területen (Az AUTOCAD pl. LISP-ben programozható). A PROLOG, vagy a magyar fejlesztésű M-PROLOG szintén ismert képviselője ennek a nyelvcsaládnak. Az e nyelveken fejlesztett alkalmazások általában a mesterséges intelligencia körébe tartoznak. és természetesen ma még nagyobb részben Neumann-elvű gépeken futnak, de találhatunk már ötödik generációs megvalósításokat is.
Zárszó, 2000 körül:
Minden előzetes jóslat és várakozás ellenére ma még mindig a Neumman-elvű gépek uralják a számítástechnikát. A nem-Neumanni architektúrák evolúciója sokkal lassúbb lett, mint ahogy azt vártuk. A hardver fejlesztések tartalékai még igen jelentősnek látszanak ahhoz, hogy ez az állapot hosszabb ideig fennmaradjon. Elmondhatjuk, hogy most, első felismerése után 40 évvel, a szoftver krízis még mindig velünk van. Bár valódi fejlődés következett be tervezési szemléletünkben, fejlesztési módszereinkben, a fejlesztői gárda oktatásában, ennek ellenére a programok iránti igény gyorsabban nőtt, mint ahogy azt a fejlesztők ki tudták volna elégíteni. Még jobb eszközökre, technikára és módszerekre lenne szükség és talán a legfontosabb: jobb oktatásra és több tapasztalatra.
Találat: 2274