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.