|
Ígéretemhez híven folytatom tervezési mintákat bemutató cikksorozatomat.
Az egyik legalapvetőbb és valószínűleg leggyakrabban használt minta az egyke minta. A neve is mutatja legfőbb jellemvonását: az adott osztályból egyetlen példány jön létre az alkalmazás futása során. Erre akkor van szükség, ha az adott objektumból felesleges, sőt zavaró lenne több példányt létrehozni; jó példa erre a hibanaplózó vagy teszem azt az XML-feldolgozó osztály. A cél tehát egy olyan módszer bevezetése, amely garantálja, hogy mindössze egy objektumot lehet létrehozni az osztályból. Az első, szembeötlő nehézség az, hogy az osztály konstruktorát bárki meg tudja hívni - ezt kell megakadályozzuk. A megoldás a konstruktor elrejtése - tegyük priváttá, és ezzel megakadályozzuk a közvetlen példányosítást. Azonban valahogy biztosítanunk kell az osztály példányosítását, hiszen különben mindössze egy használhatatlan osztályt kaptunk. Ezt a célt szolgálja az Instance() tagfüggvény, amely az egyetlen osztálypéldány létrehozásáért felelős. Az osztály egyetlen példánya az első Instance() hívás során jön létre, az ezt követő hívások pedig ezt a példányt adják vissza.
Egy Singleton osztály (avagy Egyke) létrehozásának menete: - rejtsük el a ctor-okat, a másoló ctor-t és az "operator =" -t (ezáltal lehetetlenné tesszük a közvetlen példányosítást) - deklaráljuk az adattaghoz történő hozzáférését biztosító, static public metódust (általában Instance() névre hallagat) - deklaráljuk az osztály példánymutatóját statikus privát adattagként (ez az adott osztályra egyetlen példányának címe) - gondoskodjunk arról, hogy az első hozzáféréskor létrehozzuk az egyetlen osztálypéldányt, minden azt követő híváskor pedig a már létező objektumpoinetrt adjuk vissza
Kérdés Miért statikus az Instance() metódus? Hamarosan jön a kézenfekvő magyarázat, de előbb vizsgáljuk meg a kódot:
public class Singleton { // példány private static Singleton instance; public static Singleton Instance() { // csak akkor hozzuk létre, // ha korábban ez nem történt meg if (instance == null) { instance = new Singleton(); } return instance; }; public void fv() { //... };
// ctor-ok és az értékadó operátor elrejtése, a közvetlen példányosítás megakadályozására private Singleton() {}; private Singleton( const Singleton& ); private Singleton& operator =( const Singleton& ); }
A következő kódrészlet a példányosítás módját mutatja be: // a kliensoldali kód int main() { // egyke létrehozása / lekérdezése Singleton obj = Singleton.Instance(); obj.fv() //... }
Az előző kérdésre - azaz, hogy miért static az Instance() tagfüggvény? - a fenti kódrészletben található a válasz: az osztálynak eredetileg nincs érvényes példánya, és érvényes példánymutató nélkül pedig nem tudunk meghívni nem-statikus tagfüggvényeket. A statikus függvények kivételt képeznek, hiszen az osztálypéldány megléte nem előfeltétel. (A statikus függvények korlátja, hogy kizárólag statikus adattagokon dolgozhatnak.)
Az egyik felmerülő gond a fenti Singleton megvalósítással a konkurens hozzáférés. Ha több szálból (nagyjából) egyszerre hívják az Instance() függvényt, előfordulhat, hogy az if( instance == null ) ellenőrzést egyszerre hajtják végre, majd több példány is létrejön, hiszen a feltétel egyik szálnál sem teljesült. Erre természetesen vannak megfelelő megoldások és nyelvi elemek, de a többszálúság rejtelmeibe egyelőre nem szeretnék jobban belemenni (mindazonáltal egy következő cikksorozat témája lehet, addig meg hagyatkozzatok google barátunkra ;-)).
További jó kódolást mindenkinek! Nyisztor Károly www.nyisztorkaroly.org
|