A Tervezési minták: az újrahasználható objektumorientált szoftver elemei (eredeti címén angolul Design Patterns: Elements of Reusable Object-Oriented Software) egy 1994-es szoftverfejlesztési könyv, amelyben programtervezési minták vannak részletesen leírva. A könyvet Erich Gamma, Richard Helm, Ralph Johnson, és John Vlissides írta, a hozzátartozó előszót pedig Grady Booch. A szerzőket szokás Gang of Four (GoF), azaz Négyek bandája néven együtt említeni.
A könyv két részből áll: az első két fejezet az objektumorientált programozás által nyújtott lehetőségekről és a felmerülő buktatókról szól, a maradék fejezetekben pedig 23 klasszikus tervezési mintát mutatnak be. A könyv C++ és Smalltalk nyelven tartalmaz példákat.
A könyvnek nagy jelentősége van a szoftverfejlesztés területén, és fontos forrásként tekinthetünk rá az objektumorientált tervezés elméleti és gyakorlati alkalmazásának vonatkozásában is. Több mint 500 000 példányt adtak már el angol és 13 másik nyelven.
Története A könyv egy birds of feather (BoF) beszélgetésben merült fel először a OOPSLA (Object-Oriented Programming, Systems, Languages & Applications) konferencián a 90-es években "Towards an Architecture Handbook" címmel Bruce Anderson vezetésével. Itt találkozott Erich Gamma és Richard Helm, akik felismerték, hogy mindkettőjüket érdeklik a tervezési minták. Később csatlakozott hozzájuk Ralph Johnson és John VlissidesRichard Helm is. A könyv hivatalos kiadásának dátuma 1994. október 21., de a szerzői joga 1995-ös, ennélfogva gyakran 1995-ös kiadásként utalnak rá, habár a tényleges publikálása 1994-ben volt. A nyilvánosság számára a könyv az 1994-es októberi OOPSLA konferencián lett elérhető Portlandben. 2005-ben az ACM SIGPLAN a Programming Languages Achievement Award díjat adta az íróknak, a „programozási gyakorlaton és a programozási nyelvek tervezésére”SIGPLAN FY '05 Annual Report tett nagy hatása miatt. 2012 márciusában a könyvnek már 40. nyomtatásánál tartottak.
Bevezetés, első fejezet Az első fejezete egy beszélgetés az objektumorientált tervezési technikákról az írók tapasztalatai alapján, olyanokról amik használatáról azt mondják, hogy egy jó objektumorientált szoftverdesign-t eredményez. Például: Programozz interfészre, ne implementációra! (Gang of Four 1995:18) Öröklődés helyett összetétel: Ha teheted objektum összetételt használj az osztályok öröklődése helyett! (Gang of Four 1995:20)
Az írók szerint az interfészre programozás előnye az implementációra való programozással szemben a következők: a kliensek nem ismerik az általuk használt objektumok pontos típusát, ha az objektum az interfészhez tartozik a kliensek nem ismerik mely osztályok használják ezeket az objektumokat, a kliensek csak az absztrakt osztály(ok)ról tudnak amiket az interfész definiál
Az interfész használata dinamikus kötéseket és többalakúságot eredményez, amik az objektumorientáltság alapjai.
A szerzők az öröklődésre fehér dobozos újrafelhasználásként utalnak, ahol a fehérdobozosság a láthatóságra utal, mert a szülő osztályok felépítése gyakran látható a leszármazott osztályok számára. Ezzel ellentétben az objektum összetételre feketedobozos újrafelhasználásként utalnak, mert belső információknak nem kell láthatóaknak lenniük, akkor sem, ha használjuk az objektumokat.
Az írók beszélnek az öröklődés és az egységbe zárás közötti feszültségről, és azt állítják, hogy tapasztalatuk szerint a tervezők túl sokat használják az öröklést. (Gang of Four 1995:20). Ennek veszélyeit ilyen módon fogalmazzák meg:
„Mivel az öröklődés felfedi az alosztály számára a szülő implementációját, gyakran mondják, hogy az öröklődés szembe megy az egységbe zárás elvével.” (Gang of Four 1995:19)
Figyelmeztetnek, hogy egy alosztály implementálása annyira függővé válhat a szülőosztály implementációjától, hogy bármilyen változtatás a szülő implementációjában azt eredményez, hogy muszáj az alosztályt is módosítani. Ezenkívül, elmondják, hogy ezek elkerülésére egy lehetőség, hogy csak absztrakt osztályokból van öröklődés, ugyanakkor kiemelik, hogy ennél a megoldásnál minimális kódújrafelhasználás van.
Az öröklődés használata főleg akkor javasolt, ha már létező komponensekhez új funkciót adunk, nagy részben a régi kód újrafelhasználásával és relatíve kevés új kód írásával.
A szerzők a 'delegálás'-ra az objektum összetétel egy extrém formájaként tekintenek, ami mindig használható az öröklődés helyett. A delegálás 2 objektumból áll: A 'küldő' átadja saját magát a 'delegáló'-nak, hogy az rá tudjon hivatkozni. Így a link a 2 rész között csak a futási időben jön létre, nem a fordítási időben.
A szerzők szintén beszélnek az úgynevezett paraméteres típusokról. Ezek megengedik bármilyen típus definiálását, anélkül, hogy specifikálni kellene a többi típust, amit használ. A nem specifikált típusok a 'paraméterek' a használat szempontjából.
A szerzők elismerik, hogy a delegálás és a paraméterezés is nagyon erőteljes módszerek, de figyelmeztetnek is:
„A dinamikus, nagyon paraméterezett szoftver értelmezése és építése nehezebb, mint egy statikus változaté.” (Gang of Four 1995:21)
A szerzők továbbá különbséget tesznek az 'Aggregáció' (ahol az objektumnak 'van egy' vagy 'része egy' másik objektumnak, tehát a mindkét objektumnak ugyanakkora az élettartalma) és az egyszerű 'ismeretség' között (ahol az objektum csupán 'tud' a másik objektum létezéséről). Ez utóbbit szokták 'asszociációnak' vagy 'használói' kapcsolatnak is nevezni. Az ismeretség esetén az objektumok kérhetnek egyes műveleteket egymáshoz, de nem felelősek egymásért. Az ismeretség egy lazább kapcsolat, mint az aggregáció, és egy sokkal lazább összekötést igényel az objektumok között, ami gyakran fontos lehet a lehető legjobb karbantarthatóság szempontjából.
A szerzők a 'toolkit', azaz eszközkészlet fogalmát használják, míg mások manapság talán inkább a 'class library', azaz osztály könyvtár kifejezést használják, mint például C++-ban vagy Javában. Az ő értelmezésükben az eszköztár az objektumorientált megfelelője a szubrutinkönyvtáraknak, ahol a szoftverkeretrendszer együttműködő osztályok egysége, amik újrahasználható design-t biztosítanak a szoftver egy specifikus osztálya számára. A szerzők elmondják, hogy egy alkalmazás fejlesztése nehéz, egy eszköztáré még nehezebb, a legnehezebb pedig a keretrendszer fejlesztése.
Második fejezet: esettanulmány
A második fejezet egy lépésről lépésre levezetett esettanulmánya egy WYSIWYG dokumentumszerkesztőnek, a Lexinek.
A fejezet hét problémán megy végig, amiket sorban meg kell vizsgálni a helyes design-hoz Lexi esetén, beleértve minden megkötést is amiket követni kell. Minden probléma mélységeiben meg van vizsgálva és megoldási javaslatokat is adnak. Minden megoldás részletesen be van mutatva, beleértve a pszeudokódokat, valamint Objektummodellezési technikákat, ahol szükséges.
Végül pedig, minden megoldáshoz hozzátársít egy vagy több tervezési mintát. Bemutatják, hogy a megoldás milyen formában implementálja az adott tervezési mintát.
A hét probléma (beleértve a megkötéseket is) és a megoldások (beleértve a tervezési mintára/mintákra való hivatkozásokat) a következők:
A dokumentum szerkezete A dokumentum "alapvető grafikai elemek elrendezése", mint a karakterek, sorok stb., amik "egybefoglalják a dokumentum információ tartalmát". A dokumentum szerkezete ezeknek az elemeknek az összessége és minden elem visszavezethető más résszerkezetekre.
Problémák és megszorítások A szöveg és a grafika azonos módon kell, hogy kezelve legyen (a grafika nem származtatható a szövegből és fordítva sem) Az implementáció ugyanúgy kezelje a bonyolult és az egyszerű szerkezeteket is. Az absztrakt elemek specifikus származékainak kell rendelkezni speciális analitikus elemekkel.
Megoldás és tervezési minta
A rekurzív kompozíció, elemek egy hierarchikus szerkezete, ami "fokozatosan egyre komplexebb elemeket épít az egyszerűbbekből". Minden csomópont a szerkezetben tud a szülő és a gyerek elemeiről. Ha egy művelet elvégzése az egész szerkezeten történik, akkor minden csomópont meghívja a műveletet a saját gyerekein. (rekurzívan).
Ez az összetétel tervezési mintának egy implementálása, ami csomópontoknak az összessége. A csomópont egy absztrakt alap osztály, és a tőle származhatnak levelek (egy esetén), vagy újabb csomópontokból álló kollekció(amik csomópontjai megint csak tartalmazhatnak leveleket, vagy újabb csomópont-kollekciókat). Amikor egy műveletet végrehajtanak egy szülőn, akkor a művelet rekurzív módon tovább adódik a hierarchián lefelé haladva.
Formázás A formázás nem a szerkezetet jelenti. A formázás egy módszer egy adott rész felépítésére a dokumentum fizikai struktúrája szempontjából. Ide tartozik a szöveg sorokba törése, a kötőjelek használata, a margó szélesség beállítása stb.
Problémák és megszorítások
Egyensúly a (formázás) minősége, a gyorsaság és a tárhelyfelhasználás között A formázás függetlenségének (elkülönítésének) megőrzése a szerkezettől.
Megoldás és tervezési minta
Egy Compositor osztály egységbe fogja zárni az algoritmust ami az elrendezést szolgálja. A Compositor egy alosztálya az objektumnak a dokumentum szerkezetében. Egy Compositornak van egy társított példánya a Composition objektumból. Amikor a Compositor futtatja a Compose()-t, akkor ez végig iterál minden ehhez társított elemen, és újra rendezi a szerkezetet beillesztve a megfelelő sorokat és oszlopokat.
A Compositor maga egy absztrakt osztály, ami lehetővé teszik az őt származtató osztályoknak a különböző formázó algoritmusok használatát (mint például a szélesebb margók, dubla-spacek stb.)
A stratégia programtervezési minta használható erre a célra. A stratégia egy módszer több algoritmus egységbe zárására a körülmények változására alapozva. Ebben az esetben a formázásnak különbözőnek kell lennie, attól függően, hogy szöveg, grafika, egyszerű elem stb. van-e formázva.
Felhasználói felület kezelése Annak a felhasználói felületnek (user interface) a változtatása, amin keresztül a felhasználók kapcsolatba léphetnek a dokumentummal.
Problémák és megszorítások
Szegéllyel való elkülönítése az oldal szövegének és a szerkesztési területnek Görgetősávok, amik segítségével az oldal különböző részeit tekintheti meg a felhasználó A felhasználói interfész elemi ne tudjanak a díszítésekről "Osztályok robbanásának" elkerülése, amit az alosztályok létrehozása okozna "minden lehetséges díszítési kombinációra" és elemre
Megoldás és tervezési minta
Az „átlátszó elkerítés” (transparent enclosure) segíti javítani az összetételekhez adott viselkedéseket. Az ilyen elemek, mint a szegélyek és a görgetősávok, speciális alosztályai egy-egy különálló elemnek. Mivel ezek a kiegészítések részei a struktúrának, így a megfelelő Operation() meg lesz hívva amikor a struktúra Operation()-ja meg lesz hívva. Ez azt jelenti, hogy a kliensek nincs szüksége semmilyen speciális ismeretre vagy felületre a struktúrához ahhoz, hogy használja ezeket a kiegészítéseket.
Ezt egy Díszítő tervezési minta, amely lehetővé teszi adott objektumokhoz más viselkedések hozzáadását, anélkül, hogy magát az objektumot módosítaná.
Többféle "Look-And-Feel" szabvány támogatása
A "Look-And-Feel" kifejezés a platform-specifikus UI szabványokra utal. Ezek a szabványok "meghatározzák az iránymutatásokat arra vonatkozóan, hogy egy alkalmazás, hogyan jelenik meg és hogyan viselkedik a felhasználóval"
Problémák és megszorítások
A szerkesztőnek több platformhoz érvényes szabványokat kell megvalósítania Könnyen alkalmazkodjon az új és fejlődő szabványokhoz Tegye lehetővé a futásidőben a "Look-and-feel" változásokat (Nincs kemény kód) Legyen egy készlete az absztrakt elemi alosztályokból minden elem kategóriájának (ScrollBar, Buttons stb. (görgetősáv, gombok)) Legyen egy készlete konkrét alosztályokból minden absztrakt alosztályhoz, amiknek különböző "look-end-feel" szabványai lehetnek (pl.: ScrollBar-nak MotifScrollBar)
Megoldás és tervezési minta