2024. április 26., péntek

Gyorskeresés

PHP 5.4 - trait

Írta: | Kulcsszavak: php 5.4 . php . 5.4 . trait

[ ÚJ BEJEGYZÉS ]

A PHP 5.4-es verziója még nincs készen, de már most érdeme foglalkozni az újításokkal, hiszen a szokásos hibajavítások, teljesítményfokozások mellett nyelvi újdonságokat is fog hozni magával.

A címből villámgyorsan kiderül, mivel is szeretnék most foglalkozni: trait. PHP-ban ez egy új kód-újrahasználati lehetőséget biztosít számunkra, osztályainkat tudjuk bővíteni trait-ek használatával, de nem olyan módon, ahogyan azt öröklésnél megszokhattuk. Ha egy új osztályt származtatunk egy meglévőből, akkor tulajdonképpen a szülőt vertikálisan bővítjük, ezzel szemben a trait-ek ezt horizontálisan tudják megtenni.

Miért is jó ez nekünk? Szerintem mindenkivel előfordult már, hogy 2 osztályban hasonló dolgot szeretett volna megvalósítani, de örökléssel nem igazán azt érte volna el, amit szeretne, mert a két osztálynak egyébként semmi köze egymáshoz. Kézenfekvő példa a Singleton minta. Ezt magam is sokszor eljátszottam, eljátszom, hogy az osztályaim megírását azzal kezdem, hogy belezongorázom mindbe a Singleton-hoz szükséges elemeket, amelyek gyakorlatilag minden egyes osztálynál megegyeznek, ami hatalmas pazarlást jelenthet.

Trait használatával definiálhatunk egy Singleton nevű trait-et, amit aztán minden egyes osztályunkba szimplán beemelhetünk. Működésileg a trait olyan, amint egy sima class, nincs plusz funkciója, csupán compile-kor ad egy copy-paste utasítást a rendszernek az elemek beemeléséhez az adott osztályba.

Maradjunk először a Singleton-nál, és nézzük meg, hogy a gyakorlatban hogyan működik mindez. Láthatjuk, hogy tulajdonképpen egy osztály deklaráltunk, a különbség annyi, hogy nem class, hanem trait kulcsszóval vezettük be.

Készítsünk egy A osztályt, ami használni fogja ezt a trait-et, ehhez deklarálnunk kell az osztály, majd bele kell vennünk a trait-et a use kulcsszó segítségével (a use-t használjuk a namespace-ek és bennük az osztályok használatára is):

class A
{
use Singleton;
}

Amint ez megvan, használhatjuk is:

$a = A::Instance();

Kimeneten ezt fogjuk látni: Constructor in Singleton trait - construction class of A

Ne kerülje el a figyelmünket az, hogyan is van megvalósítva a kiíratás a trait-ben, ugyanis azt láthatjuk, hogy használva van a $this, ami egyet jelent azzal, hogy trait-ekben használhatjuk az objektumváltozókat is.

Felmerülhet a kérdés, hogy pontosan hány trait-et is használhatunk egy osztályban. Szerencsénk van, ugyanis gyakorlatilag bármennyit. PHP-ban sok más nyelvhez hasonlóan nincs többszörös öröklés, és épp ez az egyik oka annak, hogy a trait-ek létrejöttek.

Készítsünk még két trait-et:

trait TraitAlpha
{
public function Hello() {
echo "TraitAlpha: Hello!\n";
}
}

trait TraitBeta
{
public function Hello() {
echo "TraitBeta: Hello!\n";
}
}

Majd próbáljuk meg használni is. Mit látunk? Mindkét trait tartalmazza a Hello nevű metódust. Ha csak simán use segítségével beemelnénk az osztályunkba, akkor nem lenne egyértelmű, hogy ha az adott példányon meghívjuk a Hello-t, melyik is fusson le, ezért:

use TraitAlpha, TraitBeta {
TraitAlpha::Hello insteadof TraitBeta;
TraitBeta::Hello as BetaHello;
}

insteadof operátor segítségével tudjuk megmondani, hogy a behúzott trait-ek közül az azonos metódusokat "melyikből húzzuk elő", itt a TraitAlpha-é lesz a feladat.
Az as operátor segítségével tudunk más nevet adni a behúzott elemeknek, itt a TraitBeta::Hello-t nevezzük át BetaHello-ra.

$a->Hello();

$a->BetaHello();

Kimeneten először látni fogjuk a TraitAlpha: Hello! majd a TraitBeta: Hello! szövegeket.

Az as segítségével nem csak egy elem nevét tudjuk megváltoztatni, hanem majdnem az összes módosítót (public, final, stb.) is használhatjuk. A static módosító nem engedélyezett, ez talán érthető, miért, de pl. átnevezéskor megadhatunk egy módosítót is. Azt nem lehet megtenni, hogy egyszerre két módosítóval is ellátjuk az adott elemet (public final például).

TraitBeta::Hello as final BetaHello;

abstract módosítónak itt nincs sok értelme, de azt fontos tudnunk, hogy trait-ek tartalmazhatnak abstract elemeket, amelyeket a felhasználó osztályban ugyanúgy kell kezelnünk, mintha egy másik osztálytól örökölte volna meg őket.

Érdekes kérdés lehet az is, mi történik, ha egy trait, egy szülő és egy gyerek osztály tartalmazza ugyanazt a metódust, melyik jut érvényre. Deklaráljunk először egy szülőt:

class B
{
public final function WhoAmI() {
echo "Class B!";
}
}

Egy árulkodó nevű trait-et:

trait OverriderTrait
{
public function WhoAmI() {
echo "OverriderTrait!";
}
}

Majd frissítsük az A osztályunkat:

class A extends B
{
use OverriderTrait;
/*
public function WhoAmI() {
echo "Class A!";
}*/
}

Egyelőre megjegyzésben van az A metódusa, nézzük, mit látunk a kimeneten, ha meghívjuk a példányunkon a WhoAmI metódust: Class B!
Vegyük ki megjegyzésből az A:: WhoAmI-t, eredmény: Class A!

Sorrendben tehát először a base metódus jutna érvényre, de ezt felülírja a trait, amit viszont felülírhat a felhasználó osztály. Amennyiben a szülő metódusa final lenne, akkor a trait sem tudná at felülírni, fatal error-ral elszállna a kódunk.

Láthatjuk, hogy a trait-ek nem visznek plusz futásidejű funkcionalitást a kódunkba, ezért gyakorlatilag minden ugyanúgy fog működni mint eddig csak osztályokat használva (pl.: late static binding).

Trait-eket össze tudunk rakni meglévő trait-ekből, ahogyan azt az osztályokban (use) láthattuk.

Letölthető kódok

Hozzászólások

(#1) Peter Kiss


Peter Kiss
senior tag
LOGOUT blog

[ értesítő ]

(#2) lezso6


lezso6
HÁZIGAZDA
LOGOUT blog

Hurrá!

A RIOS rendkívül felhasználóbarát, csak megválogatja a barátait.

(#3) j0k3r!


j0k3r!
senior tag

ismet egy korrekt cikk :R

megint varialhatom at az osztalyaimat :O :DDD

some men just wanna watch the world burn...

(#4) Peter Kiss válasza j0k3r! (#3) üzenetére


Peter Kiss
senior tag
LOGOUT blog

:R

További hozzászólások megtekintése...
Copyright © 2000-2024 PROHARDVER Informatikai Kft.