Népesedési világnap

Népesedési világnap logó

Népesedési világnap logóAz ENSZ 1987-ben július 11-ét a népesedési világnappá (World Population Day) nyilvánította. Bolygónk lakossága aznap érte el az 5 milliárdot. További kerek számok voltak: 1999. október 12-én 6 milliárd, 2011. október 30-án 7 milliárd. További kerek számok várhatóak: 2023 – 8 milliárd, 2037 – 9 milliárd, 2057 – 10 milliárd. A KSH elemzése részletes elemzéseket közöl évről-évre a témában, például: 2019-ben, 2018-ban. A worldometer.info weboldalon folyamatosan frissülő kimutatások érhetők el a népességhez globálisan, valamint országonként is: például Magyarország aktuális népesedési adatai.

A népesedési világnap inspirált egy Java program megtervezésére és megírására. A swing GUI-s program megjeleníti a worldometer.info weboldalról kinyerhető adatok alapján régiónként (kontinensenként) az elérhető adatokat 1950-től 2020-ig az alábbiak szerint egy világtérképen.

Az elkészült program

Népesedési világnap Java program

Tervezés

Objektumorientált szemlélettel, MVC architekturális tervezési mintát követünk, angol nyelvű interfész, osztály, változó, objektum, metódus nevekkel. A projekt neve: WorldPopulation, a csomag neve: worldpopulation. Amit lehet, konstansként interfészbe (szeparálva) teszünk és az MVC rétegekhez kötődő osztályok implementálják. A modell minden évszámhoz tárolja a szükséges adatokat, mindezt egyetlen betöltéssel/letöltéssel éri el. A program kliensként hat régióra vonatkozó adatot gyűjt össze, alkalmazkodva a szerver adatforráshoz. A címsorban lévő összesített adat is elérhető közvetlenül a weboldalon, de a kisebb adatforgalom érdekében hasznos inkább a kliensben összesíteni. Mindössze egyetlen eseménykezelés szükséges: a csúszka beállításával megadott évszám alapján frissíteni kell a régiók címkéit és az ablak címsorát. Öröklődés hasznos a feladat megoldása során: egyrészt interfészek, másrészt osztályok között.

Interfészek

Az ősinterfész a WorldPopulationConstants, benne az évszám intervallum MIN_YEAR és MAX_YEAR határaival, valamint a megjeleníthető régiók neveivel tömbben: REGION_NAME_ARRAY. Két utódinterfész épül az ősre: ModelConstants és ViewConstants. Előbbi interfész az adatforráshoz kapcsolódik: URL_COMMON az URL eleje, URL_ARRAY az URL végei régiónként tömbben. Utóbbi interfész a megjelenítéshez kapcsolódik: WORLD_MAP_IMAGE a háttérkép annak WORLD_MAP_RECT méretével együtt, valamint a régiónkénti REGION_RECT_ARRAY téglalapok tömbje a kezdeti pozíciókkal/méretekkel, TITLE a sablon a program címsorához (frissítendő az évszámmal és az összesített népességgel). A megfelelő utódinterfészt mindig implementálja az MVC szerint hozzá illeszkedő osztály.

Osztályok

A belépési pont a WorldPopulation.java fájlban található.

Három összetartozó elemi adatot fog össze egybe a RegionData POJO, ezek name, year, population nevű rendre String, int, long típusú adatok. Például: Európa, 2020, 747643253. Tartalmaz két függvényt: getPopulation(), valamint toString(). Utóbbi HTML formátumban adja vissza a megjelenítendő adatokat.

A JLabel-ből származik az igényekhez alakított RegionLabel osztály. Ennek van előre megadott pozíciója, mérete, betűtípusa, betűmérete, sárga háttérszíne, piros kerete. Ezenkívül a téglalap átlátszó, valamint a benne megjelenő HTML tartalom vízszintesen középre igazított. Némi extra funkció, hogy egérrel megfogva – drag and drop – áthelyezhető, ami a MouseMotionListener egérmozgást figyelő interfész mouseDragged() metódusának felülírásával válik lehetővé. A mozgathatóságáért saját maga felel. Példaként közöljük az osztály teljes forráskódját:

A webről adatokat szerez és tárolja a Model osztály, a java.io és java.net csomagokra építve. Egy példa: a https://www.worldometers.info/world-population/europe-population/ oldal forrásából nyeri ki az osztály az alábbi adatokat:

Ezek parszolását követően elkészül egy optimálisnak tekinthető, generikus listákból álló regionListArray tömb adatszerkezet. A parszolás történhet egyszerű szövegkezeléssel vagy JSON feldolgozással is. Erre épülnek a konstruktorral és vezérlővel összehangoltan működő getter metódusok: getHTML(), getRegionList(), getRegionData(), getPopulation(). A JSON adatforrás feldolgozását most nem részletezzük, de hasonlóról blogoltunk már: Időjárás Budapesten.

A grafikus felhasználói felületet adja a JFrame utód View osztály. Három GUI komponensből áll: pnWorldMap – háttérkép JPanel, lbYear – kiválasztott/aktuális év JLabel, slYear – kiválasztható/görgethető aktuális év JSlider. Izgalmas megoldani egymásra/egymáson elhelyezni a komponenseket. Egy JLayeredPane komponens  DEFAULT_LAYER rétegére kerül a térképet tartalmazó háttérkép, majd a  PALETTE_LAYER rétegére kerül dinamikusan a hat  RegionLabel osztályú/típusú objektum. A csúszka komponens slYearStateChanged() eseménykezelő metódusa vezérlőként megszólítja a modell réteget és a visszakapott adatokkal frissíti a nézet réteget (a címsorban lévő összesítéssel együtt, ezres szeparátorokkal).

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam tematikájához kötődik (ha a swing GUI-ra koncentrálunk és az adatok helyi fájlrendszerből elérhetők), és a Java EE szoftverfejlesztő tanfolyam tematikájához kapcsolódik (ha az adatokat közvetlenül a webről olvassuk).

Organogram készítése

Organogram logó

HR-organogram-logoAz Oracle HR sémából építünk organogramot, amivel megjeleníthető a szervezeti hierarchia. Személyenként készítünk csomópontokat. (Másképpen is lehetne: például részlegenként.) A megvalósítás során kétszer konvertálunk A-ból B-be. Először az adatbázisból/adatforrásból SQL lekérdezéssel jutunk hozzá a szükséges adatokhoz, amelyeket generikus listába képezzük le. Ezután a listát feldolgozva generálunk HTML fájlt, amely tartalmaz egy Organization Chart diagramot.

Hasonló feladat: Ki kinek a vezetője?, rekurzív lekérdezéssel. Érdemes összehasonlítani a kétféle szemléletmódot.

Tervezés

Most pedig azt használjuk fel, hogy az Oracle HR sémában az EMPLOYEES táblában reflexió van, amelyet az EMPLOYEE_ID és a MANAGER_ID mezők biztosítanak.

Az Organization Chartnál három adatsor adható meg. Ezek most testre szabva (mindegyik szöveges): 'Employee lastname', 'Job ID', valamint jelmagyarázatként további három mező összefűzve: 'Employee name, Department name, Job title'. Az organogramon megjelenő adatok például: "Raphaely", "PU_MAN", valamint a csomópontra fókuszálva megjelenő tooltip: "Employee: Den Raphaely, Department: Purchasing, Job: Purchasing Manager". A DEPARTMENTS táblából – az EMPLOYEES-zel a DEPARTMENT_ID-vel összekötve – megkapjuk a DEPARTMENT_NAME-t. A JOBS táblából pedig – az EMPLOYEES-zel a JOB_ID-vel összekötve – megkapjuk a JOB_TITLE-t.

A lekérdező parancs

SQL-organogram

Az EMPLOYEE_ID elsődleges kulcs, vagyis kötelező. A MANAGER_ID nem kötelező, a hierarchia tetején álló vezetőnél ez a mező null értékű. Mivel a MANAGER_ID nem kötelező, így külön lekérdező parancsban kell előállítani a 15 középvezetőt együtt a 2 felső vezetővel, valamint az egyetlen felső vezetőt, akinek a MANAGER_ID-ja null. Ezt a két részeredményt össze kell fűzni ( UNION).

Az eredménytábla

SQL-eredménytábla

Az adatfeldolgozás lépései

Java programozási nyelven kötelező a kivételkezelés a JDBC kapcsolatfelvétel, SQL parancs futtatása, valamint a fájlkezelés során. A JDBCConnection interfészben definiált szöveges konstansok: DRIVER, URL, USER, PASSWORD (az adatbázis-szerverrel való kommunikációhoz), SQL (a lefuttatandó lekérdező parancs). Az OrganizationChart interfészbe került a HTML_FILE_PATH (a generálandó HTML fájl Path útvonala) és a HTML (konstans váz az organogram testre szabott HTML+JavaScript forráskódja). Az SQL parancs ResultSet eredménytáblájának feldolgozása során áll elő az orgChartDataList generikus lista. A HTML konstans szövegben lévő #OrgChartData# elemet ki kell cserélni a generikus listából Stream API-val dinamikusan összefűzött adatokra. A fenti példa ide kapcsolódó része: "[{'v':'Raphaely', 'f':'Raphaely<div style="color:red; font-style:bold">PU_MAN</div>'}, 'King', 'Employee: Den Raphaely, Department: Purchasing, Job: Purchasing Manager']". Ezt követően a java.nio csomag Files osztályának write() metódusával fájlba menthető az előállított fájltartalom. A konkrét Java forráskódot most nem részletezem.

Az elkészült organogram

HR-organogram

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java adatbázis-kezelő tanfolyam 9-12. óra: Oracle HR séma elemzése, 13-16. óra: Konzolos kliensalkalmazás fejlesztése JDBC alapon, 1. rész, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalomhoz kapcsolódik.

Az SQL forráskód formázásához a Free Online SQL Formatter-t használtam.

Kígyókocka grafikus felületen

Kígyókocka

KígyókockaA JavaFX grafikus felhasználói felületének és eseménykezelésének megvalósítása némileg eltér más Java GUI implementációk működésétől, például swing vagy Java3D. Főként animációk során hasznos használni. Megközelítése természetesen objektumorientált: a térbeli objektumok koordinátái, viselkedésük, transzformációkkal valósul meg, és azok is objektumok. A korábban elkészített konzolos kígyókocka programot valósítjuk meg most JavaFX GUI-val.

Ez egy két részből álló blog bejegyzés 2. része. A blog bejegyzés 1. része itt található.

A program működése

Kígyókocka JavaFX grafikus felületen

A program megvalósítása

A start() JavaFX életciklust indító eljárás felépíti a createGridUI() függvényt meghívva felhasználói felületet (színpad/jelenet JavaFX-ben), beállítva a méreteket, címsort, és meghívja az eseménykezelésért felelős handleRotateButtons() eljárást.

A createGridUI() függvény a grafikus felhasználói felület elemeit paraméterezi (szerepe szerint Factory metódus). Öt elemből álló rács ( GridPane osztályú grid nevű objektum) készül el, amelyre nyilakat tartalmazó nyomógombok (pl.: Button típusú btLeft objektum) kerülnek fel a négy égtájnak megfelelően, valamint rajta középen helyezkedik el a kígyókocka 3D megjelenítését megvalósító objektum. A nyilak entitásai az Unikód karaktertáblából származnak. A kígyókocka objektumot a meghívott createSnakeCube() függvény hozza létre. A Node osztályú snakeCube nevű objektum geometriai transzformációs objektumot is hozzá kell rendelni: ez most a négyirányú forgatást megvalósítani képes névtelen Rotate osztályú objektum lesz. A forgatást 5 paraméterrel célszerű beállítani (van rá megfelelő túlterhelt konstruktor), ezek rendre: szög, X, Y, Z tengely origója és a forgatás tengelye. Az objektumok tulajdonosi hierarchiája swing-es szemmel nézve szokatlannak tűnik, de szemléletben legalább azonos a Java3D és a JavaFX megvalósítás.

A createSnakeCube() függvény előállítja a színpadra/jelenetbe kikerülő kígyókockát Node osztályú objektumként. A konstans CUBE tömb egységvektor rendszerben tartalmazza a kígyót alkotó kockák középpontjait. Az első ciklus mindezt nagyítást alkalmazva skálázza. A második ciklus koordináta és pont transzformációk alkalmazásával ( moveToMidPoint: eltolás középre, rotateAroundCenter: forgatás a középpont körül) a kiinduló állapotnak megfelelő méretben és pozícióban elhelyezi a kígyó útvonalát mutató hengerobjektumokat. A konstrukciós és transzformációs műveletek esetén alkalmazkodni kell ahhoz, hogy a JavaFX koordinátarendszerben az X jobbra, az Y lefelé, a Z pedig befelé (a nézőponttól távolodva a térben) növekszik. A matematikai hátteret részletesen most nem magyarázzuk el.

A handleRotateButtons() eljárás a forgatás 4 nyíl eseménykezelésének hozzárendelést végzi el. A nyomógomb objektumok setOnAction() hozzárendelő metódusának paramétere EventHandler funkcionális interfésszel és lambda művelettel működik. A forgatás irányát hozzárendeljük a megfelelő nyomógombhoz. Ez még csak végrendelkezés a jövőre: csak definiáljuk, hogy minek kell majd történnie, ha bekövetkezik az esemény (valamelyik nyílra/nyomógombra kattint a felhasználó).

A rotateSnake() eljárás minden nyíl feliratú nyomógombra kattintva reagál a bekövetkezett eseményre. A rotateAxis objektum a forgatás tengelye, a paraméterként átvett direction enum-tól függ, szinkronban azzal a nyomógombbal, amelyikre kattintott a felhasználó.

Ötletek a továbbfejlesztésre

  • Lehetne többféle irány is, például a négy sarokba átlós vagy mélységi irányú elforgatással.
  • Beépülhetne többféle transzformáció is, például skálázás (kicsinyítés, nagyítás), eltolás (közelítés, távolítás).
  • A kígyó útvonalát mutató hengerobjektumok kirajzolásának sorrendjén lehetne változtatni, mert a megjelenítés nem tökéletes. Jelenleg néhány helyzetben lehetetlennek, Escher lehetetlen konstrukcióihoz hasonlónak tűnhet a kígyókocka. Ha a tér mélységéből a nézőpont felé közeledve rajzolnánk ki a hengerobjektumokat, akkor a 3D látvány nem sérülne.

Tanfolyamainkon JavaFX grafikus felülettel hangsúlyosan nem foglalkozunk, de egy-egy kész forráskódot közösen megbeszélünk, és össze is hasonlítjuk a swing-es változattal. Fejlesztünk játékprogramot, de inkább konzolosan, vagy swing-es ablakban, vagy webes alkalmazásként.

A grafikus felületek felépítésének megismerése fontos lépcső az objektumorientált programozás elmélyítéséhez, gyakorlásához. A grafikus felületekhez egy másik lényeges szemléletváltás is kapcsolódik, hiszen a korábbi algoritmusvezérelt megközelítést felváltja az eseményvezérelt (eseménykezelés).

Tudatosan hangsúlyozott MVC-s projektben megoldva a feladatot, a modell rétegben tárolhatnánk többféle kígyókocka megjelenítéséhez és animációjához szükséges adatszerkezetet és transzformációs objektumokat/metódusokat is és a nézet/vezérlő rétegekben biztosíthatnánk ezek közül a kijelölést/kiválasztást menüvel, ikonokkal, eszköztárral, gyorsbillentyűkkel.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Tanfolyamaink orientáló moduljának 9-12. óra: Mesterséges intelligencia alkalmához kapcsolódóan a kígyókocka véletlenszerű előállítása helyett stratégiával rendelkező visszalépéses algoritmust specifikálhatunk és implementálhatunk.

Ez egy két részből álló blog bejegyzés 2. része. A blog bejegyzés 1. része itt található.

Letöltés szimuláció

letöltés logó

letöltés logóLetöltési folyamatot szimulálunk. A paraméterek rugalmasan beállíthatóak. Előre beállított mennyiségű adatot, párhuzamos szálakon/folyamatokon keresztül töltünk le, miközben mérjük az eltelt időt. A folyamatok állapota lehet inaktív, aktív és befejezett. Az aktív folyamatok esetében megjelenő százalék fejezi ki, hogy a folyamat hol tart a rá jutó részfeladat végrehajtásával. Összesített formában követhetjük a hiányzó és a letöltött adat mennyiségét MB-onként és százalékosan is. A folyamat szimulációjához grafikus felületű Java kliensprogram készült, egyszerű GUI komponensekkel (nyomógomb, címke, folyamatindikátor, másképpen JButton, JLabel, JProgressBar swing komponensek).

Az alábbi animáció bemutatja a letöltés szimulációját:

letölés szimuláció

A konkrét paraméterek: 128 MB-nyi adatot töltünk le 256 párhuzamos szálon/folyamaton keresztül, így egy-egy részfeladat 0,5 MB-nyi adat letöltését jelenti. Minden értéket/mérőszámot egész számként ábrázolunk, akár százalékhoz tartozik, akár mértékegységként MB vagy s. A változások – és egyben a frissítés is – 5 ezredmásodpercként történnek a GUI-n.

A Java SE szoftverfejlesztő tanfolyamunkon, a szakmai modul Objektumorientált programozás témakörét követő 29-36. óra Grafikus felhasználói felület alkalmain már tudunk egyszerűbb szimulációs programot tervezni, kódolni, tesztelni. A Java EE szoftverfejlesztő tanfolyamunkon, a szakmai modul 5-8. óra Szálkezelés, párhuzamosság alkalommal többféle elosztott stratégiát ismertetünk, és a 17-24. óra Socket és RMI alapú kommunikáció alkalommal pedig megvalósíthatjuk többféle protokoll szerint a hálózati kapcsolatot, letöltést/feltöltést.

Elosztott alkalmazások esetén többféleképpen is modellezhető és kialakítható a rendszer architektúrája. Elosztott lehet maga a hálózat, a számítási folyamat, az algoritmus. Elosztott objektumok kommunikálhatnak egyenrangúnak tekinthető P2P szerepkörben vagy szerver/kliens oldalon, és több dolog/elem/hardver/szoftver/komponens együttműködéseként is megvalósulhat elosztott alkalmazás. A hálózati kommunikáció folyamatát valamilyen protokoll határozza meg, amit minden komponens ismer és így meghatározott szabályrendszer szerint működik.

Hardver szinten elosztottak a többprocesszoros rendszerek. Szoftveresen elosztott például egy moduláris vállalatirányítási rendszer, illetve a mobilalkalmazások többsége. Tipikus háromrétegű webalkalmazás esetén külön szerver nyújtja az adatbázishoz kapcsolódó szolgáltatásokat, a felhasználó számítógépén található a böngészőben futó/megjelenő kliensprogram/weboldal és a kettő között a felhő rétegben lehet a funkcionálisan elosztott alkalmazáslogika (például validálás, titkosítás, tömörítés, autentikáció, autorizáció).

A feladat könnyen általánosítható, például:

  • Egy keresési feladatot oldjunk meg az állományrendszerben! Kereshetünk egy konkrét nevű fájlt, adott kiterjesztésű fájlt, joker karakterekkel paraméterezett nevű fájlt/mappát, adott méretű állományt, adott dátum előtt létrehozott fájlt… Az állományrendszer bejárása rekurzív módon történik. A gyökérben lévő mappánként külön, esetleg második szinten lévő mappánként külön indíthatók szálak, párhuzamos folyamatok. Ha egyetlen találat elegendő, akkor bármelyik szál pozitív visszajelzésére minden szál leállítható. A feladatnál nagy eséllyel nagyon különböző méretű mappákon és eltérő mélységű mappaszerkezeteken kell végighaladni, így erre érdemes lehet optimalizálni, de ez már nagyon más szintje ennek a problémának.
  • Active Directory szerkezetben keressünk elérhető nyomtatókat a hálózaton!
  • Elosztott számítási hálózatként működik/működött a SETI@home. Koncepciójának lényege, hogy egy hatalmas feladatot nem nagyon drága szuperszámítógépeken, hanem olcsó gépek ezrein, százezrein, vagy akár millióin végeztetjük el, amelyek jelentős szabad kapacitással (pl. processzoridővel, átmeneti tárhellyel) rendelkeznek és egyébként is csatlakoznak a világhálóra.
  • Hasonlóan elosztott működésű a torrent protokoll. A kliensek/szálak az állományokat több kisebb darabban/szeletben töltik le, természetesen párhuzamosítva. Minden csomópont megkeresi a hiányzó részhez a lehető leggyorsabb kapcsolatot, miközben saját maga is letöltésre kínálja fel a már letöltött fájldarabokat. A módszer nagyon jól beválik nagyméretű fájloknál, például videók esetében. Minél népszerűbb/keresettebb egy fájl, annál többen vesznek részt az elosztásában, ezáltal a letöltési folyamat gyorsabb, mintha mindenki egy központi szerverről töltené le ugyanazt (hiszen az informatikában minden korlátos, a sávszélesség is).
  • A képtömörítést végző algoritmusok is lehetnek elosztottak, ezáltal párhuzamosíthatóak. Például ha felosztjuk a képet 16*16-os méretű egymást nem átfedő részekre, akkor ezek egymástól függetlenül tömöríthetők.
  • A merevlemezek esetén korábban használatos defragmentáló szoftverek felhasználói felülete emlékeztet a mintafeladat ablakára.

Fontos szem előtt tartani, hogy a grafikus megjelenítés csupán a szimulációhoz tartozó – annak megértéséhez szükséges – reprezentáció, így teljesen független lehet a folyamatok valós működésétől.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Egy matematika érettségi feladat megoldása programozással 2020

érettségi logó

érettségi logóA 2020-as emelt szintű matematika érettségi feladatsor 9. feladata inspirált arra, hogy a programozás eszköztárával oldjuk meg ezt a feladatot. Szükséges hozzá kollekció adatszerkezet és néhány programozási tétel. Érdekes belegondolni, hogy mennyire más lehetne a problémamegoldás, ha programozhatnánk a matematika érettségi vizsgán. A teljes feladatsor a megoldásokkal együtt letölthető az oktatas.hu-ról.

2018-ban és 2019-ben is kiválasztottam egy-egy matematika érettségi feladatot a középszintű feladatlapról és megoldottam Java nyelven. 2020-ban az emelt szintű feladatsornál lelkesedtem eléggé, hogy blogoljak róla.

9. feladat

Egy városban a közösségi közlekedést kizárólag vonaljeggyel lehet igénybe venni, minden utazáshoz egy vonaljegyet kell váltani. A vonaljegy ára jelenleg 300 tallér. Az utazások száma naponta átlagosan 100 ezer. Ismert az is, hogy ennek kb. 10%-ában nem váltanak jegyet (bliccelnek).
A városi közlekedési társaság vezetői hatástanulmányt készíttettek a vonaljegy árának esetleges megváltoztatásáról. A vonaljegy árát 5 talléronként lehet emelni vagy csökkenteni. A hatástanulmány szerint a vonaljegy árának 5 talléros emelése várhatóan 1000-rel csökkenti a napi utazások számát, és 1 százalékponttal növeli a jegy nélküli utazások (bliccelések) arányát. (Tehát például 310 talléros jegyár esetén naponta 98000 utazás lenne, és ennek 12%-a lenne bliccelés.) Ugyanez fordítva is igaz: a vonaljegy árának minden 5 talléros csökkentése 1000-rel növelné a napi utazások számát, és 1 százalékponttal csökkentené a bliccelések arányát. A tanulmány az alkalmazott modellben csak a 245 tallérnál drágább, de 455 tallérnál olcsóbb lehetséges jegyárakat vizsgálta.

  • a) Mekkora lenne a közlekedési társaság vonaljegyekből származó napi bevétele a hatástanulmány becslései alapján, ha 350 tallérra emelnék a vonaljegyek árát?
  • b) Hány talléros vonaljegy esetén lenne maximális a napi bevétel?

Tervezés

Értelmezve a feladatot és a feltett kérdéseket: adódik, hogy a megoldáshoz szükséges egy POJO, ami az összetartozó adatokat fogja egybe objektumként. Mivel több kell belőle, célszerű egy indexelhető adatszerkezet, például tömb vagy lista. Ékezettelen magyar elnevezéseket fogok használni. A POJO osztály neve legyen Kozlekedes és a beszédes nevű tulajdonságai legyenek a következők: vonaljegyAr, napiUtasszam, bliccelesSzazalek, napiBevetel. Mindegyik nemnegatív egész szám és belefér az int primitív típus számábrázolási tartományába.

Ha a konstruktor paraméterként átveszi az input vonaljegyAr-at, akkor abból a többi adatot egyszerű képletekkel előállíthatja. Hasznos, ha a konstruktor ellenőrzést is végez. A tanulmány az alkalmazott modellben limitálja a vonaljegy árát (250 és 450 közötti öttel osztható számként). Az öttel oszthatóság az emelés/árváltozás mértékéből adódik. Ha a vonaljegy ára nem megfelelő, akkor a konstruktor kivételt dob, amivel megakadályozza, hogy az alkalmazott modellhez nem illeszkedő tulajdonságokkal rendelkező objektum létrejöjjön.

Az output meghatározásához az a) és b) feladatban megfogalmazott kérdésekből kell kiindulni. Ezekből adódik, hogy szükséges két getter metódus a POJO-ba:  getVonaljegyAr() és getNapiBevetel(). Persze könnyen generáltatható az összes getter is, de setter nem kell. Ezeken kívül a tesztelés megkönnyítésére hasznos egy toString() metódus is, amellyel a 4 összetartozó adat hozzáférhető és megjeleníthető a konzolon.

A belépési pont és egyben a vezérlés egy másik osztályban valósul meg. Itt feltöltjük a tanulmány alkalmazott modelljének megfelelően előállított objektumokkal (memóriacímeikkel) a generikus listát, amit programozási tételekkel (kiválasztás, szélsőérték-kiválasztás) dolgozunk fel.

A POJO osztály forráskódja

A vezérlő osztály forráskódja

A main() metódus feltölti a generikus lista adatszerkezetet az alkalmazott modellben lehetséges/előforduló vonaljegyAr alapján létrehozott objektumokkal (a memóriacímükkel). A feladat9Megoldas1() metódus paraméterként átveszi a feldolgozandó listát.

Az a) feladatra a választ kiválasztás programozási tétellel kapjuk meg. A kérdés így szól: melyik az (első) olyan objektum, amelyben a vonaljegyAr egyenlő 350-nel? A ciklust követően megkapjuk, hogy az i-edik az, amelyikre igaz a feltétel. (Az nem merül fel, hogy van-e ilyen objektum, hiszen tudjuk, hogy van. Csak az a kérdés, hogy melyik az. Több sem lehet.) A  lista.get(i).getNapiBevetel() művelettel elkérjük az i-edik objektumtól a válaszadáshoz szükséges napi bevételt.

A b) feladatra a választ szélsőérték-kiválasztás programozási tétellel kapjuk meg. A kérdés így szól: melyik az (első) olyan objektum, amelyben a napiBevetel a maximális? (Mivel a lista nem üres, így létezik a legnagyobb napi bevétel. Mivel nem biztos, hogy a legnagyobb napi bevétel egyedi, ezért merül fel az első a kérdésben.) Tegyük fel, hogy a nulladik objektumra igaz a feltétel: azaz maxIndex=0. Később a ciklusban változtassuk meg a maxIndex-et, ha a feldolgozás során találunk nagyobb értéket. Szélsőérték-kiválasztásnál a kezdeti elemet nem hasonlítjuk össze saját magával (hiszen úgysem különbözne), ezért indul a for ciklus 1-ről. A ciklust követően a  lista.get(maxIndex).getVonaljegyAr() művelettel elkérhetjük a maxIndex-edik objektumtól a válaszadáshoz szükséges vonaljegy árát.

A program által felépített adatszerkezet

Ha a vezérlőben aktiváljuk a megjegyzésben szereplő kiíratást, akkor a konzolon megjelennek a main() metódusban létrehozott listában lévő objektumok adatai (amilyen viselkedést a POJO toString()-jébe programoztunk. A 246 soros szöveg görgetéssel megtekinthető.

Az eredmény

A program konzolon/szövegesen jeleníti meg a válaszokat a feltett két kérdésre:

Gondoljuk újra

Az első megoldás 41 elemű listát épít. Persze ez a lista több mindenre is jó lehet, ha több(féle) kérdést kell(ene) megválaszolni. Ezért tekinthetjük strukturális tartaléknak.

A két konkrét kérdésre azonban úgy is adhatunk választ, hogy nem építünk lista adatszerkezetet. Ez a második megoldás. A feladat9Megoldas2() metódusnak nincs paramétere és azonos eredmény ad.

Az a) feladat: egy névtelen objektumként létrehozott POJO-tól azonnal elkérhetjük a választ, ami mehet rögtön a konzolra. Ez a kiválasztás programozási tétel extrém/legjobb esete, hiszen az első objektum jó is lesz, ciklust sem kell szervezni.

A b) feladat: kiindulunk a legolcsóbb vonaljegyből és tegyük fel, hogy ekkor a legnagyobb a napi bevétel. Ciklussal léptessük a vonaljegy árát ötösével legfeljebb a legdrágábbig. Léptetés közben mindig csak azt a dinamikusan létrehozott objektumot „jegyezzük meg”, amelyiktől a röptében elkért napi bevétel a korábbihoz – az addig legnagyobbnak vélthez – képest nagyobb. Végül a megmaradó POJO-tól elkérhető a maximális napi bevételhez tartozó vonaljegy ára. Ez a szélsőérték-kiválasztás programozási tétel megvalósítása dinamikusan: kezdetben nem áll rendelkezésre az összes adat, ami alapján döntést kell hozni, ehelyett az adatokat menet/feldolgozás közben állítjuk elő és „eldobjuk” azt, ami már nem kell.

Nekem ezek a programozással való megoldások sokkal jobban tetszenek, mint az oktatas.hu-n elérhető hivatalos, matematikai megoldás, amihez differenciálszámítás is kell. Persze aki emelt szinten érettségizik matematikából, annak az sem jelenthet gondot és biztosan izgalmasnak találja.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 5-8. óra: Vezérlési szerkezetek, 9-12. óra: Metódusok, rekurzió, valamint 17-24. óra: Objektumorientált programozás alkalmaihoz kötődik.

2018-ban és 2019-ben is kiválasztottam egy-egy matematika érettségi feladatot a középszintű feladatlapról és megoldottam Java nyelven.