UML¶
Figyelem
Az itt található anyag otthoni tanulásra, feldolgozásra van kitalálva, így ez a gyakorlaton sem kerül leadásra!
A UML (Unified Modeling Language) egy nagyon általános nyelv, melyet a szoftvertervezés és modellezés során alkalmazunk. Nagyon sokféle diagramot lehet elkészíteni a segítségével, ebben a példában az osztálydiagram (Class diagram) és csomagdiagram (Package diagram) kerül bemutatásra. Akit érdekel mit lehet még UML-lel modellezni itt és itt olvashat utána.
Osztálydiagram¶
Az osztály leírást ad objektumok egy csoportjához, ezeknek közösek az attribútumaik (adattagjaik), operációik, más objektumokkal való kapcsolataik (publikus metódusaik) és szemantikus viselkedésük. Az osztályra gyakorlatilag mintaként tekinthetünk, ami meghatározza a példányosítás során létrejövő objektumo(ka)t. Ezek alapján a UML diagramon az osztályoknak feltüntetjük az adattagjait, metódusait valamint a kapcsolatokat is.
Kapcsolatok¶
Az osztálydiagramon kapcsolatok segítségével jelöljük az osztályok közötti kapcsolatokat, a majdani objektumok kölcsönhatásait. Ezek a kapcsolatok határozzák meg javarészt a programunk viselkedését. Az osztálydiagramon 3 fő kapcsolat fajtát különböztetünk meg:
- Asszociáció: legalább az objektumok egyike ismeri a másikat, de (általában) létük nem függ egymástól.
- Aggregáció: speciális asszociáció, amikor az egyik objektum része a másiknak (létük függ egymástól).
- Kompozíció: speciális aggregáció, amikor a tartalmazott a tartalmazó nélkül nem létezhet.
A következő ábrán tekinthetjük meg melyik kapcsolatot hogy jelöljük UML-ben. Fontos, hogy aggregáció és kompozíció esetében mindig a tartalmazó osztály felől legyen a szimbólum.
Öröklődés¶
Osztályok közötti kapcsolat, amikor egy osztály strukúráját / viselkedését megörökli egy vagy több másik osztály. A kapcsolatban résztvevő osztályokat szülő osztálynak (az ős, aki megosztja a struktúráját) és gyerek (aki megörökli a struktúrát) osztály(ok)nak nevezzük. A származott (gyerek) osztály mindent örököl a szülőtől, ezeket kiegészítheti sajátok tulajdonságokkal/operációkkal. Illetve egy örökölt viselkedést is megvalósíthatunk másképp a leszármazott osztályokban. Az öröklödés szülő-leszármazott kapcsolat, de tekinthetünk rá úgy, hogy öröklődési hierarchián felfelé menve (megnézzük az ősosztályt, majd annak ősét, stb.) általánosítást, leszármazottak felé haladva (megnézzük a gyerekosztályt, aztán annak a gyerekét, stb.) pedig specializálást figyelhetünk meg.
Csomagdiagram¶
Nagy rendszereknél elkerülhetetlen az osztályok csoportosítása. Erre használhatók a csomagok (package), a segítségükkel hierarchikus szerkezetet biztosíthatunk a kódunknak. A csomagok áttekintésére használhatunk csomagdiagramot, melyet szintén UML-ben készíthetünk el. A csomagban lévő osztályok adattagjait/metódusait is fel lehet tüntetni, mint osztálydiagram esetében, azonban ha így túl zsúfolt lenne az ábra azok elhagyhatók. Az is elképzelhető, hogy nem az egész rendszert ábrázoljuk így, csak a benne szereplő 1-2 csomagot, vagy a csomagokat külön-külön. Szerencsére a UML rugalmas annyira, hogy ezt mind megtehetjük. A csomagdiagram lényege, hogy egy absztraktabb, magasabb szintű képet kapjunk a rendszerről.
Feladat¶
Modellezzük (egy egyszerűsített) osztálydiagrammal és csomagdiagrammal egy online webáruházat, majd valósítsuk is meg Pythonban! Az áruházba jöhetnek vevők, akik megrendelhetnek különböző árucikkeket. Mindenféle árut árulnak, szóval törekedjünk az általánosságra. Többféle fizetési módot is szeretnénk támogatni, ez akár a jövőben bővülhet is. Az alábbiakban a diagram tervezésével párhuzamosan szemléltijük a Python implementációt.
Megoldás¶
Először is készítsük el az osztályokat a leírás alapján. Ez viszonylag egyszerű, hiszen tudjuk hogy lesznek vevők, rendelés, árucikkek és fizetni is kell valahogy. Ezek alapján a következő osztályaink lesznek (adattagokkal és metódusokkal együtt, amik valószínűleg kelleni fognak):
Pythonban osztályt a class
kulcsszó segítségével tudunk létrehozni, az adattagokat az __init__
metódusban elhelyezve adhatunk az osztálynak. Az első osztály, amit elkészítünk, a Vevő osztály lesz.
1 2 3 4 5 |
|
A Rendelés osztály esetében a kifizet metódust egyelőre nem fejtjük ki, csak az egyszerűbbeket.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Az Árucikk osztályban megfigyelhető a getter/setter használata Python-ban. Mivel arra törekszünk, hogy szép rendezett kódot írjunk, így az Árucikk osztályt (és a majd belőle származtatott gyerek osztályokat) külön könyvtárba helyezzük (ez Python esetében azt is jelenti, hogy külön csomagba tesszük).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
A Fizetés osztályban a fizet metódust csak megadtuk, hogy létezik, viszont nem írtunk semmit a törzsébe. Így szimulálhatjuk a máshonnan ismeretes absztrakt metódust és absztrakt osztályt. Pythonban is van egyébként erre nyelvi támogatás, ez azonban nem része tananyagnak, akit érdekel itt és itt tud bővebben olvasni róla. A Fizetés osztályt szintén külön csomagba szervezzük.
1 2 3 4 5 6 |
|
Mivel többféle árucikk lehet a webáruházban és többféle fizetési módot is támogatni szeretnénk, hozzunk létre még 2 fajta árucikket és 2 féle fizetési módot. Hogy kihasználjuk az OOP előnyeit és törekedjünk az általánosításra, a már fent létrehozott Árucikk és Fizetés osztályokból fogjuk ezeket örököltetni.
A Lézerkard osztálynál láthatjuk, hogy a szülő konstruktorának hívásakor a tömeg paraméter értéke mindig 1, viszont az osztály bővült a szín adattaggal.
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 8 |
|
Hogy az adott kártyaszám helyes-e, egy reguláris kifejezés segítségével ellenőrizzük. Erről bővebben itt olvashatunk, illetve más tantárgyak esetében is találkozhattunk már vele.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Az elkészült projekt mappaszerkezete a következő: - main.py (majd itt fogjuk kipróbálni amit csináltunk) - Rendeles.py (a rendelést reprezentáló osztály) - Vevo.py (a vevőt reprezentáló osztály) - Arucikk (könyvtár - csomag) - Arucikk.py (ősosztály) - GumiKalapacs.py (gyerek osztály) - Lezerkard.py (gyerek osztály) - Fizetes (könyvtár - csomag) - Fizetes.py (ősosztály) - KartyasFizetes.py (gyerek osztály) - Keszpenz.py (gyerek osztály)
Már csak a kapcsolatokat kell meghatároznunk az osztályok között. A Rendelés osztály köti össze a Fizetés-t a Vevő-vel és itt egyik esetben sem beszélhetünk tartalmazásról, ezek csak "ismerik egymást", kommunikálnak egymással. Így ezek között asszociáció lesz a megfelelő kapcsolat. A rendelés viszont tartalmazza az árucikkeket, viszont annak léte nem függ az árucikkektől. Így itt egyszerű aggregációról van szó. Az elkészült osztálydiagramot a következő ábrán lehet megtekinteni.
Az objektumok kölcsönhatása, vagyis az asszociáció megvalósulása legfőképp a kifizet metódusban nyilvánul meg. Ennek törzse a következőképp néz ki:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Bár a feladat bonyolultsága nem indokolja a csomagdiagram külön ábrázolását (és a benne lévő osztályok leegyszerűsítését), a példa kedvéért elkészítettük ezt is. Figyeljük meg a csomagok közötti kommunikációt!
Készítsünk is egy egyszerű tesztet a programhoz! Figyeljünk az importokra, az árucikkek külön csomagban vannak.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Kimenet
Anakin Skywalker készpénzzel kíván fizetni. Sikertelen fizetés! :(
Obi-Wan Kanobi kártyával (('5133-3367-8912-3456', '02/22')) kíván fizetni. Sikeres fizetés! A megvásárolt árucikkek: Lézerkard