Singleton tervezési minta

Azt már áttekintettük, hogy mik azok a tervezési minták és milyen típusaik léteznek, de vannak olyan sűrűn előforduló tervezési minták, melyeket érdemes részletesen megvizsgálni, hogy egy adott implementáció esetében a legmegfelelőbb mintát alkalmazzuk.
A Singleton tervezési minta

Azt már áttekintettük, hogy mik azok a tervezési minták és milyen típusaik léteznek, de vannak olyan sűrűn előforduló tervezési minták, melyeket érdemes részletesen megvizsgálni, hogy egy adott implementáció esetében a legmegfelelőbb mintát alkalmazzuk. Most a singleton mintával ismerkedünk meg közelebbről.

Mire jó a singleton minta? Ezt használjuk annak érdekében, hogy az erőforrás igényes osztályból létrehozható példányok számát csak egyre korlátozzuk.

Az erőforrásigényes osztályok olyan osztályok, amelyek lelassíthatják webhelyünket vagy pénzbe kerülhetnek. Például:

  • Egyes külső szolgáltatók (API-k) minden használatért pénzt számítanak fel.
  • Egyes mobileszközöket észlelő osztályok lelassíthatják webhelyünket.
  • Az adatbázissal való kapcsolat létrehozása időigényes, és lelassítja az alkalmazásunkat.

Tehát ezekben az esetekben jó ötlet az erőforrás igényes osztályból létrehozandó objektumok számát csak egyre korlátozni.

A Singleton minta megvalósítása

Kezdjük azzal, hogy megértsük egy olyan osztály szerkezeti jellemzőit, amely megfelel a singleton mintának:

  1. Privát konstruktort használnak az osztályból objektumok közvetlen létrehozásának megakadályozására.
  2. A költséges objektum létrehozási folyamatot a privát konstruktor végzi.
  3. Az egyetlen módja annak, hogy példányt hozzon létre az osztályból, ha statikus metódust használ, amely csak akkor hozza létre az objektumot, ha még nem jött létre.

Talán ez az egyik legismertebb minta, ami egyben antipattern-nek is számít, mert:

  • globális változókat hoz létre, amelyek a kódban bárhonnan elérhetők és megváltoztathatók
  • Sérti az egy felelősség elvét (Single responsibility). A minta egyszerre két problémát old meg.

Mikor használjuk?

A singleton minta használata indokolt azokban az esetekben, amikor korlátozni akarjuk az osztályból létrehozandó példányok számát a rendszer erőforrásainak megtakarítása érdekében. Az ilyen esetek közé tartoznak az adatbázis-kapcsolatok, valamint a rendszererőforrásainkat felemésztő külső integrációk.

Implementáció

class Singleton {
  // A konkrét osztálypéldányt tárolja
  private static $instance = null;
  
  // Privát konstruktor

  private function __construct()
  {

  }
 

  public static function getInstance()
  {
    if (self::$instance == null)
    {
      self::$instance = new Singleton();
    }
 
    return self::$instance;
  }
}

Mint látható a konstruktor privát hozzáférésű, azaz csak a tartalmazó osztályban érhető el.

A getInstance metódus pedig csak akkor hoz létre egy új példányt a Singleton class-ból, ha az $instance osztályváltozó még null értékű, azaz nincs még létrehozva egyetlen példány sem.

Ha már létezik példány egyszerűen csak visszatér vele egy új példány létrehozása nélkül.

A Singleton tervezési minta arról híres, hogy korlátozza a kód újrafelhasználását és bonyolítja az egységtesztet. Azonban bizonyos esetekben még mindig nagyon hasznos. Különösen akkor, ha bizonyos megosztott erőforrásokat kell vezérelnie. Például egy globális naplózó objektum, amelynek vezérelnie kell a naplófájlhoz való hozzáférést. Egy másik jó példa: egy megosztott futásidejű konfigurációs tároló.

Mi ezzel a probléma, ha hatékony erőforrás szempontból?

A singleton tervezési minta sérti a S.O.L.I.D elvek közül a Single responsibility, azaz az egy felelősség elvét, ugyanis két problémát old meg egyszerre:

  1. Meg kell győződni arról, hogy egy osztálynak csak egyetlen példánya van. De miért akarná bárki is szabályozni, hogy egy osztálynak hány példánya legyen? Ennek leggyakoribb oka a megosztott erőforrásokhoz – például egy adatbázishoz vagy fájlhoz – való hozzáférés szabályozása.

    Hogy kell ezt elképzelni? Tegyük fel, hogy létrehoztunk egy objektumot, de egy idő után úgy döntünk, hogy újat hozunk létre, mert máshol is kell. A singleton segítségével ahelyett, hogy új instance-t kapnánk, azt kapjuk meg, amelyet már korábban elkészítettünk, így spórolunk az erőforrásokkal.

    Ez a viselkedés normál konstruktorral azért nem valósítható meg, mert a konstruktor feladata mindig egy új példány létrehozása.
  2. Globális hozzáférési pontot biztosít az adott példányhoz az alkalmazás bármely pontjáról. A globális változók bár nagyon hasznosak, nagyon nem biztonságosak, mivel bármely kód felülírhatja a változók tartalmát és összeomolhat az alkalmazás.

    Csakúgy, mint egy globális változó, a Singleton tervezési minta lehetővé teszi, hogy a program bármely pontjáról hozzáférjen bizonyos objektumokhoz. Ugyanakkor megvédi azt a példányt attól, hogy más kód felülírja.

Alkalmazhatóság

A Singleton tervezési mintát akkor alkalmazzuk, ha a program egy osztályának csak egyetlen, ugyanazon példánya kell minden ügyfél számára. Például egyetlen adatbázis-objektum, amelyet a program különböző részei használnak.

A Singleton minta letiltja az összes többi módszert az adott osztály objektumának létrehozására, kivéve ezt a speciális létrehozási módszert. Ez a metódus vagy új objektumot hoz létre, vagy visszaadja a meglévőt, ha az már korábban létrejött.

A globális változókkal ellentétben a Singleton minta garantálja, hogy egy osztálynak csak egyetlen egy példánya van. A Singleton osztályon kívül semmi sem helyettesítheti a gyorsítótárazott példányt.