Traity w PHP: współdzielenie metod między klasami bez dziedziczenia

Traity w PHP: współdzielenie metod między klasami bez dziedziczenia

W PHP 5.4 pojawiła się w języku nowa konstrukcja: „trait”. Dzięki traitom klasy PHP mogły wreszcie odziedziczyć metody i właściwości z kilku źródeł naraz. Bezpośrednia odpowiedź na problem dziedziczenia jednokrotnego.

Oto przykład:

php
<?php

trait PersonTrait
{

}

Jak widzisz, trait wygląda bardzo podobnie do klasy. Różnica polega na tym, że deklaruje się go słowem kluczowym trait. Dla traitów obowiązuje konwencja nazewnicza: dopisujemy sufiks Trait. Trzymaj się jej przy pisaniu własnych traitów, wtedy struktura projektu pozostaje czytelna.

W odróżnieniu od klasy, i to jest rzecz do zapamiętania, traitu nie da się rozszerzyć ani zaimplementować. Nie da się go też zinstancjonować. Traity mają jeden jedyny cel: uzupełniać nasze klasy, a nie je zastępować.

Traity mogą mieć metody, dokładnie tak jak klasy. Wyobraź sobie klasę Person.

php
<?php
abstract class Person
{

}

Nasza klasa Person jest klasą abstrakcyjną. Znaczy to, że ma być rozszerzana przez inne klasy reprezentujące osoby.

Załóżmy teraz, że chcemy dodać metodę fireball(). Na przykład po to, żeby rzucać kulami ognia i uprawiać magię. Moglibyśmy napisać coś takiego.

php
<?php
abstract class Person
{
    public function fireball()
    {
        echo 'This Person send a fireball';
    }
}

Problem w tym, że nie chcesz, żeby kulą ognia rzucał każdy, i tu do gry wchodzą traity.

Traity świetnie nadają się do dokładania metod obiektowi. Chcemy dać trait tym ludziom, którzy mają magiczne moce, szukamy więc czegoś w rodzaju PowerTrait

php
<?php

trait PowerTrait
{
    public function fireball()
    {
        echo 'This person send a fireball.';
    }
}

Przenieśliśmy metodę fireball() do traitu. Żadnej innej różnicy tu nie znajdziesz. Oznacza to również, że możemy usunąć tę metodę z Person, o tak.

php
<?php
abstract class Animal
{

}

Utwórzmy klasę Elfe, która dziedziczy po Person.

php
class Elfe extends Person
{
    private $name="Legolas"; // Tak, wiem, Legolas nie rzuca kulami ognia, ale tylko on przyszedł mi do głowy...
}

Rozszerzyliśmy Person klasą Elfe, choć na razie jest pusta. Załóżmy, że elfy potrafią rzucać zaklęcia: idziemy dalej i użyjemy traitu PowerTrait

php
<?php

class Elfe extends Person
{
    use PowerTrait;

private $name="Legolas";
}

Słowo kluczowe, którym dokłada się trait do klasy, to use, umieszczamy w niej deklarację use PowerTrait;

Spróbujmy wywołać metodę odziedziczoną z naszego traitu.

php
<?php

// Utwórz nową pandę rudą.
$legolas = new Elfe;

// Pogłaszcz pandę.
$legolas->fireball();

Na razie użyliśmy tylko jednego traitu, ale nic nie stoi na przeszkodzie, żeby użyć tylu, ilu potrzebujesz. Na przykład coś takiego jest jak najbardziej możliwe.

php
<?php

class Elfe extends Person
{
    use PlayerTrait;
    use PowerTrait;
    use AttributTrait;

private $name="Legolas";
}

Używaj traitów, żeby rozbudowywać możliwości swoich klas. Oczywiście masz je do dyspozycji dopiero od PHP >= 5.4, miej to na uwadze.

Pierwszeństwo

Może zadajesz sobie takie pytanie. Co się dzieje, gdy rozszerzamy klasę i jednocześnie używamy traitu, który ma metodę o tej samej nazwie?

Zobacz na przykładzie:

php
<?php
trait TestTrait
{
    public function test()
    {
        echo 'Méthode d\'un trait';
    }
}

class TestClass
{
    public function test()
    {
        echo 'Méthode d\'une classe';
    }
}

Mamy tutaj TestTrait i TestClass. Obie te konstrukcje mają metodę test(), która mówi, skąd przyszło wywołanie.

Czas na test:

php
<?php

class Testing extends TestClass
{
    use TestTrait;
}

$test = new Testing;

$test->test();

Napisaliśmy klasę, która rozszerza klasę TestClass i używa TestTrait. Wywołując wspólną metodę test(), po wyniku poznamy, która z nich ma pierwszeństwo. Sprawdźmy.

code
Méthode d'un trait

widzimy, że metoda z traitu ma pierwszeństwo przed metodą z klasy, po której dziedziczymy.

Znaczy to, że traitami możemy nadpisywać funkcjonalność odziedziczoną po klasach abstrakcyjnych. Dobrze to rozumieć, jeśli zamierzasz pracować z traitami, warto też wiedzieć, kiedy używać traitu w PHP, na to pytanie odpowiada osobny artykuł na blogu.

PHP

Damien Flandrin Web developer od 2010 roku, twórca Gekkode i Email Impact. Każdy artykuł jest sprawdzany na prawdziwym projekcie przed publikacją. Kontakt
Newsletter

Nowe testy, poradniki i projekty, e-mailem.

Powtarzalne testy, wersjonowany kod, datowane wyniki. Nigdy spamu.