A Konstruktor, mint létrehozási minta

Az objektum-orientált nyelvekben a Konstruktor egy speciális metódus, amit az újonnan létrehozott objektum inicializálására használunk. A Konstruktorban rejlő lehetőségeket fogjuk megvizsgálni PHP és Javascriptben egyaránt. Folytatás a cikkben.

A Konstruktor nem csak létrehozza az új objektumot, de ha paramétereket adunk neki, azt felhasználva beállíthat az objektumban tulajdonságokat és metódusokat – amikor az objektum létrejön.

A PHP nyelvben a Javascripttől eltérően vannak osztályok és öröklődés, ezt a továbbiakban mindig tartsuk szem előtt.

Kontruktorok a PHP-ben

A szülőosztály (a továbbiakban csak szülő) konstruktora nem hívódik meg abban az esetben, ha a (származtatott) gyermekosztálynak (a továbbiakban csak gyermek) definiáltunk külön konstruktort. Ez esetben nekünk kell gondoskodni - amennyiben szükség van rá - arról, hogy a szülő konstruktora meg legyen hívva (parent::__construct();). Ha a gyermeknek nincs definiálva külön konstruktor, akkor az megkapja a szülő konstruktorát.

Megjegyzés, ha a PHP5 nem talál __construct() függvényt az adott osztályban, és az osztály nem is örökölt egyet a szülőtől, akkor megpróbálja megkeresni a régi-stílusú konstruktort, aminek a neve megegyezik az osztály nevével. Ez egy kompatibilitási okokból megtartott eljárás.

Objektumlétrehozás

class AlapOsztaly {
}
$obj = new AlapOsztaly();

Az így létrejött objektumnak nincs semmije, nincs adattagja, és nincsenek metódusai sem.

Konstruktor használata, egyszerű értékadás példa

class AlapOsztaly {
  private $nev = "";
  function __construct($nev){
    $this->nev = $nev;
  }
  function HogyHivnak(){
    echo $this->nev;
  }
}
$obj = new AlapOsztaly("alap osztaly");
// alap osztaly
$obj->HogyHivnak();

Kicsit kibővítettük az AlapOsztaly-t, kapott egy privát adattagot ($nev) ami a nevét fogja tartalmazni, alapesetben ez üres, mivel nem tudjuk előre, hogy milyen nevet fog kapni menetközben.

Az objektum készítésekor meg is adjuk annak nevét, amit a konstruktor fog átadni az adattagnak, azt pedig majd a HogyHivnak() függvény éri el és íratja ki.

A konstruktor és az öröklés

class AlapOsztaly {
  function __construct() {
    echo "Alap osztály konstruktora.";
  }
}
class GyermekOsztaly extends AlapOsztaly {
  function __construct() {
    parent::__construct();
    echo "Gyermek osztály konstruktora.";
  }
}
class GyermekOsztaly_A extends AlapOsztaly {
  function __construct() {
    echo "Gyermek osztály [A] konstruktora.";
  }
}
class GyermekOsztaly_B extends AlapOsztaly {
  // öröklés az alap osztályból
}
// Alap osztály konstruktora.
$obj = new AlapOsztaly(); // Alap osztály konstruktora.
// Gyermek osztály konstruktora.
$obj = new GyermekOsztaly(); // Gyermek osztály [A] konstruktora.
$obj = new GyermekOsztaly_A(); // Alap osztály konstruktora.
$obj = new GyermekOsztaly_B();

A példa jól mutatja, hogy az AlapOsztaly-ban létrehozott konstruktort szépen lefut, amint elkészül az Objektum belőle.

A GyermekOsztaly-nál szintén elkészítettük az osztály saját konstruktorát, amely meghíváskor le is fut, valamint látható, hogy a parent::__construct(); sor meghívja a szülő konstruktorát, ez a sor lehet a konstruktoron belül bárhol, nem kell rögtön az elejére tenni. Így a szülő és a gyermek konstruktor is lefut.

A GyermekOsztaly_A-nál jól látszik, ha a parent::__construct(); sort kihagyjuk, akkor csak a gyermek konstruktor fut le.

Végül pedig a GyermekOsztaly_B mutatja, hogy ha elhagyjuk a gyermeknél a konstruktordefiniálást, akkor az megkapja (örökli) a szülő konstruktorát.

A dologban jártasabbaknak bizonyára sok kérdőjel forog a feje fölött, hogy mitől tervezési minta a Konstruktor. Márpedig OO megközelítésben mindenképp mintának tekinthető, legfeljebb annyira alap minta, hogy már rutinná vált és nem úgy tekintünk rá. Mindenképpen érdemes használni, érdemes jól használni.

Konstruktorok a Javascriptben

Na, itt kezdődnek az érdekességek, mivel a Javascript egy – e tekintetben - nagyon rugalmas nyelv, viszont nincsenek benne a PHP-ból ismert osztályok és az öröklés (helyette van prototype).

Objektumlétrehozás

Az objektumlétrehozásnak 3 egyszerű példája van a Javascriptben, mind a három egy üres objektumot készít, éspedig:

var Obj = {};
var Obj = new Object();
var Obj = Object.create(null);

Ahol az "Object" készíti az objektumot, illetve ahol nincs érték adva ott mindig egy üres objektumot kapunk eredményül.

Értékadás példák

A pont szintakszis, feltételezzük a már elkészített objektumot (lehet üres is). (ECMAScript 3)

Obj.kulcs = "valami érték"; // értékadás
var kulcs = Obj.kulcs; // érték lekérdezés

Értékadás négyzetes zárójellel (ECMAScript 3)

Obj["kulcs"] = "valami érték"; // értékadás
var kulcs = Obj["kulcs"]; // érték lekérdezése

Értékadás az Object.defineProperty segítségével (ECMAScript 5)

// értékadás
Object.defineProperty(Obj, "kulcs", {
  value: "valami érték",
  writable: true,
  configurable: true,
  enumerable: true
}); // érték lekérdezése, ahogy eddig is...

Kis kitérőt tennék a defineProperty-nél, ha ezzel adunk új tulajdonságot az objektumhoz, akkor nem csak egy egyszerű kulcs-érték párt hozunk létre, hanem sokkal jobban kontrollálhatjuk is az így létrehozott tulajdonságot.

A value tartalmazza a tulajdonság értékét, valamint:

  • writable: ha false, akkor a tulajdonság értéke nem írható felül.
  • configurable: ha false, akkor a törlés vagy változtatás sikertelen lesz
  • enumerable: ha true, akkor a ciklusokban felsorolható lesz

Picit bonyolultnak tűnhet egy ilyen értékadás, de ha megnézzük a következő átalakítás után, máris sokkal szimpatikusabb lesz.

// tulajdonság létrehozó
var ujTulajdonsag = function(obj, key, value){
  var config = {value: value, writable: true, enumerable: true, configurable: true};
  Object.defineProperty(obj, key, config);
};
// létrehozzuk az üres objektumot
var ember = Object.create(null); // értékeket adunk neki
ujTulajdonsag(ember, "hajszin", "szőke");
ujTulajdonsag(ember, "szemszin", "kék");

Értékadás az Object.defineProperties segítségével (ECMAScript 5)

// objektum elkészítése
var ember = Object.create(null);
// értékek megadása
Object.defineProperties(ember, {
  "hajszin": {value: "szőke", writable: true},
  "szemszin": {value: "kék", writable: true}
});

A defineProperties egyszerre több tulajdonságot definiál, de azt hasonlóan teszi mint a defineProperty (nem véletlen az elnevezés).

Ami az öröklődést illeti, íme, egy gyors példa az előzőekből tanulva.

// az előző ember objektumot felhasználva, készítünk egy tanulo objektumot
var tanulo = Object.create(ember);
// adunk neki egy új tulajdonságot
ujTulajdonsag(tanulo, "magassag", "180");
// lekérdezzük az örökölt tulajdonságot
console.log(tanulo.hajszin);
// lekérdezzük az új tulajdonságot
console.log(tanulo.magassag);

Alap konstruktor

Ahogy már az elején is írtam, a Javascript nem rendelkezik az osztály koncepcióval, de támogatja a konstruktor függvényeket, így egy egyszerű "new" hívással mondhatjuk a Javascript-nek, hogy példányosítson egy új objektumot a függvényben definiált adattagokkal, metódusokkal együtt.

A konstruktorban a this kulcsszóval hivatkozhatunk az új leendő objektumra. Tekintsük ezt át egy rövid kóddal:

// az Ember objektum konstruktora
function Ember( hajszin, szemszin, magassag){
  this.hajszin = hajszin;
  this.szemszin = szemszin;
  this.magassag = magassag;
  this.toString = function(){
    return "haja: "+this.hajszin+", szeme: "+this.szemszin+" és "+this.magassag+"cm magas";
  }
};
// példányosítunk két embert, egy pisti-t és egy jozsi-t
var pisti = new Ember("barna", "barna", 170);
var jozsi = new Ember("szőke", "kék", 180);
// lekérdezzük a tulajdonságaikat
console.log(pisti.toString());
console.log(jozsi.toString());

A fenti mutatja, hogy milyen egyszerű a konstruktor minta, de az öröklés picit nehézkes és például a toString() függvényt mindig újra definiálja akárhányszor új objektumot készít az Ember konstruktor. Szerencsére van egy kellemesebb megoldás is.

Konstruktor Prototype-val

A javascriptben a függvényeknek van egy prototype tulajdonsága. Amikor meghívunk egy Javascript konstruktort, hogy készítsen egy objektumot, a konstruktor prototípusának minden tulajdonsága elérhető lesz az új objektumban.

Így több Ember objektumot tudunk készíteni, amelyek mind ugyanazt a prototípust érik el. Nézzük meg a példánkat most a prototype segítségével.

// az Ember objektum konstruktora
function Ember( hajszin, szemszin, magassag){
  this.hajszin = hajszin;
  this.szemszin = szemszin;
  this.magassag = magassag;
};
// a toString() függvényt a prototype-val készítjük el
Ember.prototype.toString = function(){
  return "haja: "+this.hajszin+", szeme: "+this.szemszin+" és "+this.magassag+"cm magas";
};
// példányosítunk két embert, egy pisti-t és egy jozsi-t
var pisti = new Ember("barna", "barna", 170);
var jozsi = new Ember("szőke", "kék", 180);
// lekérdezzük a tulajdonságaikat
console.log(pisti.toString());
console.log(jozsi.toString());

Így már a toString() függvény elérhető az összes Ember objektumból.

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.

Avast vírusírtó letöltés ingyen