• Nem Talált Eredményt

Hogyan válasszunk tervezési mintát?

A szerződés alapú tervezés alapelvei

A szerződés

2. GoF tervezési minták katalógusa

2.2. Hogyan válasszunk tervezési mintát?

Tervezési minták

A különféle mintakatalógusokban található tervezési minták közül nem könnyű megtalálni, melyikre van éppen szükségünk egy adott probléma megoldásához, különösen, ha a gyűjtemény új és ismeretlen számunkra. Éppen ezért tekintsünk át néhány javaslatot ([GOF1994, GOF2004] nyomán) arra vonatkozóan, hogy milyen irányelvek mentén végezzük a megfelelő tervezési minta kiválasztását:

Vegyük figyelembe, hogy a tervezési minták hogyan oldják meg a tervezési problémákat.

Nézzük át a célról szóló részeket. A különféle mintakatalógusok a 2. szakasz - GoF tervezési minták katalógusa részben leírtakhoz hasonlóan rendre felsorolják a tartalmazott (leírt) minták céljait. Olvassuk át ezt a részt, azok után a célok kutatva, amelyek kapcsolódnak az adott problémához. A minták osztályozása szintén jó támpontot adhat a tekintetben, hogy melyek a szóba jöhető minták.

Tanulmányozzuk a minták kapcsolatát. Ezt nagy mértékben meg tudja könnyteni, ha az egyes minták közötti kapcsolatok grafikusan is megjelennek, mint ahogyan az a 4.1. ábra - Tervezésiminta-kapcsolatok GOF2004 ábrán is látható. Ezen kapcsolatok tanulmányozása segíthet a jó minta vagy mintacsoport megtalálásában.

Tanulmányozzuk a hasonló célú mintákat. A katalógus három fejezetből áll: az első a létrehozási mintákról szól, a második a szerkezeti mintákról, a harmadik pedig a viselkedési mintákról. Minden fejezet a mintákat bemutató megjegyzésekkel indít, és egy olyan résszel zárul, ami összehasonlítja a mintákat. Ezek a részek a hasonló célú minták közti hasonlatosságokba és különbségekbe engednek betekintést.

Vizsgáljuk meg az újratervezés okait. Vizsgáljuk meg az újratervezés okait, hogy tisztában legyünk vele, hogy pontosan miért van szükség rá. Aztán nézzük át a mintákat, amelyek segíthetnek elkerülni az újratervezést.

Az újratervezés gyakori okai

Annak érdekében, hogy olyan rendszert tudjunk tervezni, amelyet egy-egy (például a követelményekben bekövetkező) változás esetén nem feltétlenül kell rögtön újratervezni, tisztában kell lennünk azzal, hogy milyen tipikus okai vannak a változásnak. Az a terv, amelyik ezeket nem veszi figyelembe, a későbbi alapos újratervezés szükségességének kockázatát hordozza magában.

Megjegyzés

Persze akármilyen gondosan és előrelátóan is végezzük a tervezést, a későbbiekben felmerülhetnek olyan igények, amelyek akár a legjobb tervet is változatásra kényszerítik. Itt tehát a célunk csak az lehet a változás lehetséges okainak vizsgálatával, hogy megpróbáljuk csökkenteni annak esélyét, hogy ilyenre szükség legyen.

A tervezési minták használata abban tud segítséget nyújtani, hogy a rendszer meghatározott módokon módosítható legyen. Mivel minden tervezési mintában vannak a rendszerszerkezetnek olyan részei, amelyek a többitől függetlenül változtathatóak, így a megfelelő minták alkalmazásával a rendszert ellenállóbbá tudjuk tenni bizonyos változásfajtákkal szemben.

Az újratervezést szükségessé tévő leggyakoribb okok az alábbiak:

Az objektumlétrehozást úgy végezzük, hogy konkrét osztálytól való függőséget alakítunk ki. Ez mindig megtörténik, ha az objektumaink létrehozására a new operátort használjuk. Ekkor, ha a későbbiekben egy másik osztály példányát kellene létrehoznunk (például valamely később létrehozandó alosztályét vagy az ugyanazon interfészt megvalósító valamely másik osztályét), akkor bizony módosítanunk kell a kódot. Ez tulajdonképpen szembemegy az interfészre programozzunk, nem implementációra alapelvvel, hiszen a new után egy konkrét megvalósítást jelentő osztály neve szerepel. Ehelyett az Elvont gyár (Abstract Factory), a Gyártófüggvény (Factory Method) vagy éppen a Prototípus (Prototype) minta alkalmazásával elérhetjük, hogy az objektumok létrehozása közvetett módon történjen, így megszüntetve a konkrét implementációs osztálytól való függőséget.

Minderre persze csak akkor lesz szükségünk, ha várható, hogy a későbbiekben majd új típusú objektumok is megjelennek, azok létrehozására is szükség lesz.

Konkrét műveletektől való függőség. A helyzet az előzőhöz hasonló, csak nem egy konkrét objektum létrehozásától, hanem egy konkrét művelet elvégzésétől függünk, amely a kérésnek egyfajta kielégítési módját jelenti. A Felelősséglánc (Chain of Responsibility) és a Parancs (Command) minták segítségével függetleníthetjük magunkat a megváltoztathatatlanul kódolt kérésektől.

Tervezési minták

Hardver- és szoftverkörnyezettől való függőség. A különféle hardverelemekkel, operációs rendszerekkel és egyéb szoftverkomponensekkel történő kapcsolattartás során azzal szembesülhetünk, hogy különböző környezetekben más-más lehet a kommunikáció módja. A környezetétől függő programot nehéz más környezetekhez illeszteni (vagyis a hordozhatóság csorbát szenved), ezért például az Elvont gyár (Abstract Factory) vagy a Híd (Bridge) minta alkalmazásával tehetjük programunkat környezetfüggetlenebbé.

Az objektumok reprezentációjától és megvalósításától való függőség. Ha egy kliens tudja, hogy egy objektum reprezentációja, tárolása, megvalósítása hogyan történik, és ráadásul ki is használja ezt a tudást, függővé válik: ha maga az objektum megváltozik, az is változtatásra szorul. Ez persze bezárási problémát jelent, de hát különböző bezárási szintekről beszélhetünk. Az Elvont gyár (Abstract Factory), a Híd (Bridge), az Emlékeztető (Memento) és a Helyettes (Proxy) minták azok, amelyek alkalmazásával az objektumunk részleteit elrejthetjük a kliensek elől, így növelve a függetlenséget, és segítve objektumaink rugalmas változtathatóságát.

Algoritmikus függőségek. Az algoritmusok hatékonyságát sokszor utólag finomhangolják, kibővítik, vagy éppen teljesen lecserélik őket a fejlesztés és újrafelhasználás során. Az ilyen algoritmusoktól függő objektumok kénytelenek lesznek megváltozni az algoritmus megváltozásakor, ezért azon algoritmusokat, amelyek valószínűsíthetően meg fognak változni, érdemes elkülöníteni. Erre az Építő (Builder), a Bejáró (Iterator), a Sablonfüggvény (Template Method), a Látogató (Visitor) és talán leginkább a Stratégia (Strategy) minták szolgálnak.

Szoros csatolás (tight coupling). A lazán csatoltság (loose/low coupling) ellentéte. Azokat az osztályokat, amelyek egymással szoros viszonyban vannak (vagyis egymástól függenek), nehéz – vagy akár lehetetlen – egymástól függetlenül újrafelhasználni. Ez azt eredményezi, hogy nem változtathatunk meg vagy cserélhetünk le egy osztályt a rendszerben anélkül, hogy sok másik osztály működését is meg ne értenénk, vagy akár át ne írnánk. Rendszerünk így nehezen áttekinthetővé és módosíthatóvá válik. Az Elvont gyár (Abstract Factory), a Híd (Bridge), a Felelősséglánc (Chain of Responsibility), a Parancs (Command), a Homlokzat (Façade), a Közvetítő (Mediator) és a Megfigyelő (Observer) minták használata abban segít minket, hogy csökkentsük a csatolás szorosságát.

A működés alosztályokkal történő kibővítése. Egy létező osztály funkcionalitásának alosztályokkal történő kibővítése nem egyszerű feladat, mert egy alosztály létrehozásához a szuperosztály mély megértésére van szükség (hiszen nemcsak a szerkezet, de a viselkedés is öröklődik). Például egy metódus felüldefiniálása más metódus(ok) felüldefiniálását is maga után vonhatja (mint ahogyan az Object-ből örökölt equals és hashCode esetében is elvárás). A kompozíció és a delegáció az alosztályokkal történő kibővítés rugalmas alternatívái lehetnek. hiszen anélkül tudunk új szolgáltatásokat adni a rendszerhez, hogy új alosztályok sokaságát kellene létrehoznunk. A kompozíció túlzott használata persze nehezíti a megértést, éppen ezért tehet jó szolgálatot a Híd (Bridge), a Felelősséglánc (Chain of Responsibility), az Összetétel (Composite), a Díszítő (Decorator), a Megfigyelő (Observer) vagy éppen a Stratégia (Strategy) minta, amelyek sokszor csak egyetéen alosztály létrehozását javasolják, míg a további funkciókat a meglévő objektumok kompozíciójával állíthatjuk elő.

Osztályok kényelmetlen módosítása. Néha olyan osztály megváltoztatására van szükség, amelyet bonyolult volna (például a változás rengeteg alosztály megváltoztatását is magával vonná), vagy akár nem is lehetséges (mert esetleg ez egy harmadik féltől származó – úgynevezett third-party – osztálykönyvtár, amelynek nem rendelkezünk a forrásával). Az Illesztő (Adapter), a Díszítő (Decorator) és a Látogató (Visitor) minták ezen módosítások elvégzését egyszerűsítik le.

Gondoljuk át, mit tegyünk változtathatóvá rendszerünkben. Ez a megközelítés az újratervezés okaira való összpontosítás ellentéte. Ahelyett, hogy azt néznénk, mi miatt kellhet megváltoztatnunk a tervet, azon gondolkodunk el, hogy mit akarunk majd újratervezés nélkül módosíthatóvá tenni. Itt a változó elemek egységbe zárására összpontosítunk, ami számos tervezési minta témája. Az 4.4. táblázat - A tervezési minták által megengedett változtatható elemek táblázat azokat az elemeket sorolja fel, amelyeket az egyes tervezési minták használatával függetlenül, vagyis újratervezés nélkül megváltoztathatunk.

4.4. táblázat - A tervezési minták által megengedett változtatható elemek

Cél Tervezési minta Változtatható elemek

Tervezési minták

Cél Tervezési minta Változtatható elemek

Létrehozási

Elvont gyár Leszármazott objektumok családjai Építő Hogyan készül az összetett objektum Gyártófüggvény Egy példányobjektum alosztálya Prototípus Egy példányobjektum osztálya Egyke Egy osztály egyetlen példánya

Szerkezeti

Illesztő Egy objektum interfésze

Híd Egy objektum megvalósítása

Összetétel Egy objektum szerkezete és összetétele

Díszítő Egy objektum kötelességei leszármaztatás nélkül Homlokzat Felület egy alrendszerhez

Pehelysúlyú Objektumok tárolásának költsége

Helyettes Hogyan érünk el egy objektumot; az objektum helyzete

Viselkedési

Felelősséglánc Az objektum, ami a kérelmeket teljesíti Parancs Mikor és hogyan teljesül egy kérelem Értelmező Egy nyelv szabályai és értelmezése

Bejáró Hogyan érjük el és járjuk be egy aggregátum elemeit Közvetítő Mely objektumok hatnak egymásra, és hogyan

Emlékeztető Milyen privát információ tárolódik az objektumon kívül, és mikor Megfigyelő Számos más objektumtól függő objektum; hogyan maradnak a függő

objektumok naprakészek Állapot Egy objektum állapotai Stratégia Egy algoritmus Sablonfüggvény Egy algoritmus lépései

Látogató Olyan műveletek, amelyek alkalmazhatók objektum(ok)ra az osztályuk megváltoztatása nélkül