Szoftverarchitektúrák - Jegyzet
I. Szolgáltatás-hozzáférési és konfigurációs minták
I.1. Wrapper Facade
Nem OO API-k elé egy OO réteget húzunk, ami nyújt bizonyos szolgáltatásokat, ami felhasználja az alatta levő réteget. Így az alacsony szintű API helyett egy OO-sat hívunk, ez kényelmesebb és hordozhatóbb. Hátránya, hogy a funkcionalitás csökkenhet, valamint valószínűleg lassabb lesz a kód.
I.2. Component Configurator
Egy szolgáltatás interface-re több implementáció is létezhet, amelyek közül nem feltétlenül tudjuk kiválasztani fejlesztés közben a legjobbat. Ezzel a mintával elkészítjük az összes implementációt, és futási időben választjuk ki, hogy melyiket használjuk. A komponenseknek kell az életciklusukat kezelni, tehát elindítani és leállítani. Hátránya, hogy nem determinisztikus, valamint bonyolultabb lesz a kód.
Szereplői:
- Komponens: Szolgáltatást leíró interface
- Konkrét komponensek: Az interface-t megvalósító osztályok
- Komponenstár (repository): Tárolja a konkrét komponenseket
- Komponens konfiguráló: A komponensekhez konkrét komponenst rendel, és ezt futásidőben tudja változtatni.
I.3. Interceptor
Az interceptor egy olyan eseménykezelő, amit a keretrendszer eseményeire lehet kötni. Hasonlít az AOP-hez.
Szereplői:
- Konkrét keretrendszer (framework): Egy generikus architektúra
- Elfogó (interceptor): Olyan interface, amely az egyes eseményekre bekövetkező eseménykezelőnek meg kell valósítania
- Konrét elfogó: Interceptor implementációja
- Diszpécser (dispatcher): Eseményekhez van rendelve, ehhez pedig lehet konkrét elfogó. Ez fogja hívni az interceptort.
- Kontextus objektum: Az eseményt és a keretrendszert lehet rajtuk keresztül elérni az elfogóból
- Alkalmazás: A keretrendszeren futó alkalmazás
I.4. Extension Interface
Akkor jó, ha úgy kell kiterjeszteni egy osztályt, hogy a kliensen ne kelljen változtatni. Példakódhoz lásd: http://stackoverflow.com/questions/1055833/need-citation-for-extension-interface-pattern-in-java .
Szereplői:
- Komponens: Ezt szeretnénk kiterjeszteni
- Kiterjesztő interface: A komponens szerepeit tartalmazza
- Gyökér interface: Minden komponensnek meg kell valósítania, ettől lehet lekérni a kiterjesztő interface-kat, valamint közös szolgáltatásokat is adhat.
- Kliens: Aki hívja
- Komponensgyár: A gyökeret lehet tőle kikérni
II. Eseménykezelési minták
II.1. Reactor
Lásd: https://wiki.sch.bme.hu/bin/view/Infoszak/OotTervezesiMintak#Reactor
Szereplői:
- Kezelő: OS biztosítja, ami jelzést tud adni, pl hálózati kapcsolat.
- Szinkron esemény szétválasztó: Addig blokkolódik, amíg nincs jelzés
- Eseménykezelő interface: Megadja, hogyan kell az eseményt feldolgozni
- Konkrét eseménykezelő: Az előző interface-t megvalósítja
- Reaktor interface: Eseménykezelőt lehet hozzáadni és eltávolítani
II.2. Proactor
A reactor aszinkron változata, híváskor a kezelőt aszinkron módon hívja meg, és amikor az visszatér, akkor kikeresi a kezelőt, amivel választ tud küldeni.
II.3. Asynchronous Completion Token (ACT)
Mint a proactor, csak a választ kezelőt megkapja a feldolgozó, ezért nem kell kikeresni. Leírás: http://en.wikipedia.org/wiki/Proactor_pattern
II.4. Acceptor-Connector
- Acceptor: Passzívan várakozik bejövő kapcsolatra
- Connector: Aktívan felép egy kapcsolatot
A kapcsolat felépítése után várakoznak eseményre, amit valamelyik másik minta fog kezelni. Azért jó, mert leválasztható vele az, hogy éppen klienst vagy szervert írunk.
III. Konkurenciakezelési minták
III.1. Active Object
Lásd: https://wiki.sch.bme.hu/bin/view/Infoszak/OotTervezesiMintak#Akt_v_objektum
III.2. Monitor Object
Lásd: https://wiki.sch.bme.hu/bin/view/Infoszak/OotTervezesiMintak#Monitor
III.3. Half-Sync/Half-Async
Lényege, hogy hardverfejlesztők aszinkron műveleteket szeretnek, szoftveresek meg szinkronokat. Ezzel a mintával megoldható, hogy mindenki úgy programozzon, ahogy szeretne.
Szereplői:
- Aszinkron réteg: Az aszinkron hívások végrehajtásáért felel
- Szinkron réteg: A szinkron hívások végrehajtásáért felel
- Üzenetkezelő réteg: A másik 2 réteg üzenetekkel kommunikál egymással, ezen keresztül
- Eseményfigyelő: Eseményre meghívja az aszinkron réteget
III.4. Leader/Followers
Lásd: https://wiki.sch.bme.hu/bin/view/Infoszak/OotTervezesiMintak#Vezet_k_vet
IV. Szinkronizációs tervezési minták
IV.1. Scoped Locking
Felhasználja, hogy a C++-ban amikor kilép a vezérlés az objektum scope-jából, akkor lefut a destruktora. Így a lock egy Guard osztály, aminek a konstruktorában lefoglalódik a zár, a destruktorában felszabadul. Így a zárolt részt bárhogyan hagyjuk el, biztosan nem marad zárolva.
IV.2. Strategized Locking
A lényege, hogy a lock objektumot paraméterként megadhatóvá tesszük, így a védett objektum létrehozásakor megadhatjuk, hogy milyen módon zárolható (R/W lock, mutex, stb...). Ehhez kell egy ősosztály, amiből a különböző implementációkat származtatjuk.
IV.3. Thread-Safe Interface
A probléma az, hogy egy osztályon belüli metódushívásoknál többször is zárolni akar, akkor self-deadlock alakul ki. Ezért elválasztjuk a publikus metódusok hívását és az implementációjukat private metódusokba tesszük, és minden publikus metódus elején zárolunk, a többinél azonban nem. Így ha a függvények egymást hívják, nem lesz újrazárolás, viszont minden bejöbő hívás zárolt lesz.
IV.4. Double-Checked Locking Optimization
Ha egy kritikus szakasznak pontosan 1-szer szabad lefutnia, akkor zárolás után is meg kell győződnünk arról, hogy még nem futott-e le. Ez pl. Singletonok létrehozásánál lehet fontos, mivel a létrehozó fv-t több szál is meghívhatja egyszerre, ekkor ha csak záraink vannak, akkor egymás után több példányt is létrehoznak belőle.
Pl:
static SingletonClass *GetInstance() { //Az első ellenőrzés. if (instance == 0) { //Szinkronizálás. Guard guard(lock); //A második ellenőrzés. if (instance == 0) instance = new SingletonClass(); } return instance; } }