A Singleton (Egyke) minta

Sokat vitatott minta, van, aki az Ördög művének tekinti, van, aki szereti használni. Egy biztos, nem véletlenül lett tervezési minta belőle, de az biztos, hogy oda kell figyelni rá.

A sorozat 3. részeként, most a Singleton minta kerül bemutatásra. Lényege, ahogy a neve is mutatja, hogy csak egy példány hozható létre az objektumból. Valamint fontos, hogy lehetőleg mindenhonnan elérhető legyen.

A cikkben ismét PHP-s és Javascriptes oldalról is megközelítjük a mintát, annak megvalósítását.

A cikk feltételezi, hogy az előző részben említett konstruktor mintát már ismerjük.

Mielőtt belegabalyodnánk, tisztázzuk, hogy mely esetekben hasznos ez a minta. Előfordulnak olyan esetek, amikor kifejezetten előny, ha egy objektumból csak és csakis egy példány létezik, ilyenek lehetnek például:

  • naplózási feladatokat ellátó osztály
  • adatbázis műveleteket végző osztály
  • felhasználó kezelő osztály (a belépett felhasználókkal kapcsolatos tevékenységekért felel, login, logout stb…)
  • egy webshop kosara (nem lehet csak egy bevásárlókosara egy embernek)
  • az oldal/tartalom előállításért felelős osztály

Mindenekelőtt felhívnám a figyelmet arra, hogy minden feladat, más és más megoldást kíván, ezért az itt felsorolt példákra sem mindenesetben igaz az, hogy Singletonnal kell megcsinálni, csupán lehetőség.

De miért jó, ha csak egy van ezekből?

A válasz egyszerű, erőforrás csökkentés (adatbázist kezelő osztály) például, de természetesen akár biztonsági oka is lehet (webshop kosara). Ha az adatbázis kezelést Singletonnal oldjuk meg, akkor akár mennyi adatbázis műveletet végeztetünk is el, mindig csak a végrehajtó objektum egyetlen példányát kell dolgoztatni (memóriabarát verzió). A webshop kosarát tekintve pedig, egy felhasználónak egy időben csak egy kosara lehet, így megelőzzük azt, hogy "véletlenül" egy új kosarat példányosítsunk neki, ami ugye nem tartalmazná az addig belepakolt termékeket.

PHP-s Singleton minta

<?php
  class Obj{
    // a példány tárolója
    private static $instance = null;
    // privát tag
    private $adat;

    private function __construct($adat = null) {
      if(isset($adat)) {
      $this->adat = $adat;
      }
    }
    // publikus példányosító metódus
    // ha már van példány visszaadjuk, ha még nincs, akkor elkészítjük
    public static function getInstance($adat = null) {
      if(!isset(self::$instance)) {
        self::$instance = new Obj($adat);
      }
      return self::$instance;
    }
    // hozzáférés a privát adat taghoz
    public function adatLekero() {
      return $this->adat;
    }   } // példány lekérése (inicializálással)
  $o1 = Obj::getInstance("alma");
  // lekérjük az $adat tartalmát
  var_dump($o1->adatLekero());
  // string(4) "alma" // példány ismételt lekérése (inicializálással)
  $o2 = Obj::getInstance("almaspite");
  // lekérjük az $adat tartalmát
  var_dump($o2->adatLekero());
  // string(4) "alma" ?>

Amint látjuk, az osztályunknak van egy $instance (példány) változója, ebben tároljuk magát az objektumpéldányt. Amikor lekérünk egy példányt, megvizsgáljuk, hogy már létezik-e az objektumpéldány, ha nem, akkor példányosítunk egyet, majd azt eltároljuk a példány változóban ($instance) és visszaadjuk azt. Ha már létezik az objektumpéldány, akkor csak visszaadjuk azt. Így kizártuk annak lehetőségét, hogy több példány jöjjön létre.

A konstruktor priváttá tétele megakadályozza, hogy magunk példányosítsunk, valamint a klónozást is jó kizárni egy üres, de privát __clone() függvénnyel.

A példa végén a rövid működési teszt jól mutatja, hogy értelemszerűen mind a két ($o1 és $o2) adatlekérő függvénye ugyanazt kell, hogy visszaadja, hiszen csak egy példány létezik.

A Javascriptes Singleton minta

var Obj = (function(){
  // a példányunk (privát) változója
  var _instance;
  // A példányt inicializáló metódus
  function getPeldany(adat){
    // az adat változónk (privát)
    var _adat = adat;

    return {
      // a publikus adatLekero, ami hozzáfér a privát _adat-hoz
      adatLekero: function () {
        return _adat;
      }
    };
  };   return {
    // Lekérjük a példányt, ha létezik, vagy csinálunk egyet, ha még nem
    getInstance: function(adat){
      if(!_instance){
        _instance = getPeldany(adat);
      }
      return _instance;
    }
  };
})(); // példányosítunk "alma" kezdő értékkel
var o1 = Obj.getInstance("alma");
console.log(o1.adatLekero());
// "alma" // példányosítunk "körte" kezdő értékkel
var o2 = Obj.getInstance("körte");
console.log(o2.adatLekero());
// "alma"

Nos, a Javascriptes Singleton egy picivel másképp néz ki, ez ugye a nyelvi sajátosságok és a PHP-val szembeni különbségekből adódnak. Maga a Singleton objektum rendelkezik egy _instance (példány) változóval, amit nevezhetünk privát tagnak is, mivel kívülről nem hozzáférhető (egységbezárás), valamint, egy szintén privátnak tekinthető getPeldany() metódussal is, ami inicializálja a példányt. Az objektumunk visszatér az egyetlen hívható getInstance() függvénnyel, ami egyben vissza is tér a példánnyal (_instance). A példány fogja tartalmazni az immár publikus adatLekero()-t. Természetesen a példányunk tetszőlegesen bővíthető, és elhagyható az inicializáló paraméter (adat) is.

A végén a használatnál jól látszik, hogy az o1 és o2adatLekero() metódusa is mindig a létrehozáskor kapott paramétert tartalmazza, vagyis az "alma"-t.

Ahogy az elején említettem sok esetben kifejezetten jól jön egy ilyen, csak és kizárólag csak egy példányú objektum. Azonban legyünk körültekintőek, ha menetközben változik a projektünk, lehet, hogy különböző nehézségek adódnak a használata miatt. Ezért is tervezzünk jó előre!

Ha tetszett a cikk, véleményed van, kiegészítenéd vagy hibát találtál, írj a cikk alatt egy kommentet, vagy írd meg emailben

Buy and Trade Bitcoin at Binance