Dziedziczenie w PHP

Dziedziczenie w PHP

W dziedziczeniu między klasami jest podobnie jak w dziedziczeniu między ludźmi.

Twój potomek odziedziczy po Tobie pewien majątek. Od teraz będzie dysponować zarówno swoim majątkiem, jak i Twoim. Tak samo klasa potomna odziedziczy po przodku zestaw pól i metod. Do tego będzie mogła posiadać własne, o których przodek nie miał pojęcia.

Jeśli klasa bazowa posiada pola i metody, które nie są prywatne, to klasa dziedzicząca również będzie je posiadać. Zobacz poniższy przykład:

<?php
class Samochod {
  private $numerSeryjny = 123;
  protected $czystosc = 10;
  public $model;

  public function czyJechacNaMyjnie() {
    return this->czystosc < 5;
  }

  public function wjedzNaTrawe() {
    $this->pobrudz(1);
  }

  protected function pobrudz($iloscBrudu) {
    $this->czystosc -= $iloscBrudu;
    if ($this->czystosc < 0)
      $this->czystosc = 0;
  }
}

class Jeep extends Samochod {
  private $rysy = 0;
  public function wjedzNaBloto() {
    $this->pobrudzIPorysuj(1);
  }

  private function pobrudzIPorysuj($iloscBrudu) {
      if ($this->czystosc > 0) // tutaj mamy dostęp do pola czystosc, gdyż jest protected
        $this->pobrudz($iloscBrudu); // oraz do metody pobrudz, gdyż jest protected

      $this->rysy += 1;
  }
}

$terenowy = new Jeep();
$terenowy->model = 'Grand Cherokee'; // pole model odziedziczone z klasy Samochod
$terenowy->wjedzNaTrawe(); // podobnie jak metoda wjedzNaTrawe()
$terenowy->wjedzNaBloto();
if ($terenowy->czyJechacNaMyjnie()) // ta również jest odziedziczona!
  echo 'Jedziemy na myjnie!';

$czystosc = $terenowy->czystosc; // ta linijka wysypie błąd, gdyż z zewnątrz nie mamy dostępu do pola
$terenowy->pobrudz(1); // ta linijka podobnie, jak wyżej

?>

Przeanalizujmy powyższy kod.

Mamy prostą klasę samochód, która definiuje trzy pola i trzy metody. Jedno z pól, numerSeryjny, jest prywatne, więc nie będzie do niego dostępu z zewnątrz ani z klas dziedziczących.

Pole model jest publiczne, więc jest otwarte na modyfikacje. Będzie tak samo otwarte w klasie dziedziczącej.

Pole czystosc jest protected (z ang. „chronione”) stąd będzie dostępne jedynie w klasie bazowej oraz klasach dziedziczących.

Tak samo jest z metodami.

Teraz czas na klasę dziedziczącą, naszego Jeepa.

Klasa dziedzicząca – rozszerzająca

Właściwie, gdyby stricte trzymać się tłumaczeń angielsko-polskich, powinniśmy użyć terminu klasa rozszerzająca. Ma to wiele sensu, gdyż klasa dziedzicząca bierze funkcjonalność klasy bazowej i rozszerza ją o dodatkowe pola i metody.

By skorzystać z dziedziczenia, należy po nazwie klasy użyć słowa kluczowego extends, po czym podać nazwę klasy bazowej. W luźnym tłumaczeniu wygląda to mniej więcej tak:

Klasa Jeep rozszerza klasę Samochod

W klasie Jeep pojawia się dodatkowa funkcjonalność w postaci dwóch nowych metod – prywatnej i publicznej, oraz nowego pola prywatnego.

Zauważ, że w klasie Jeep możemy korzystać z pól i metod publicznych oraz protected klasy Samochod. Dzięki temu mamy przygotowaną wspólną bazę, do której możemy dokładać kolejne typy samochodów, zachowując niezmienioną część wspólną.

Jeśli będziesz chciał dołożyć dodatkową funkcjonalność do Twoich samochodów – np. tankowanie i poziom paliwa, możesz to zrobić w klasie bazowej. Wtedy wszystkie pozostałe typy potomne będą miały dostęp do tych parametrów.

Ograniczenia w dziedziczeniu

Warto wspomnieć o potencjalnych ograniczeniach w dziedziczeniu.

Przde wszystkim, klasa może rozszerzać tylko jedną klasę bazową (dziedziczyć po jednej klasie). W praktyce wygląda to tak, że nie możesz napisać:

<?php
class Drzewo {
  protected $kolor;
}

class Roslina {
  protected $iloscWody;
}

class Brzoza extends Drzewo, Roslina {
  // taka konstrukcja jest niemożliwa w PHP
}
?>

Rozszerzanie na raz dwóch klas jest niedozwolone w PHP, choć taka konstrukcja jest dozwolona np. w języku C++.

W następnej lekcji dowiesz się, czym są modyfikatory dostępu i jak ukryć pole klasy przed ingerencją z zewnątrz.

Nadpisywanie metod i słowo kluczowe final

Może się tak zdarzyć, że Twoja klasa potomna będzie definiować metodę o identycznej nazwie jaką definiuje klasa bazowa. Co wtedy?

Najzwyczajniej w świecie nowa metoda przysłoni metodę bazową. Stracisz dostęp do bazowej, gdyż pod tą nazwą kryje się już nowa funkcjonalność.

Zobacz przykład:

<?php
class Samochod {
  public function przedstawSie() {
    echo "Jestem Samochodem";
  }
}

class Terenowy extends Samochod {
  public function przedstawSie() {
    echo "Jestem Terenówką";
  }
}

$terenowy = new Terenowy();
$terenowy->przedstawSie(); //wyświetli Jestem Terenówką
?>

To zupełnie normlana sprawa i dość popularna sytuacja w programowaniu. Są nawet metody, które wymuszają nadpisanie metody przez klasę potomną. O tym jednak w kolejnych lekcjach.

Teraz chciałbym Ci pokazać, w jaki sposób możesz zabronić klasom potomnym nadpisywania danej metody:

<?php
class Samochod {
  final public function przedstawSie() {
    echo "Jestem Samochodem";
  }
}

class Terenowy extends Samochod {
  public function przedstawSie() { // Tutaj poleci błąd, gdyż Terenowy nie może nadpisać metody oznaczonej jako final
    echo "Jestem Terenówką";
  }
}
?>

Używając słowa kluczowego final możesz zabronić nadpisywania metod.

Możesz też użyć tego modyfikatora dla całej klasy, wtedy żadna inna klasa nie będzie mogła po niej dziedziczyć.

Zobacz:

<?php
final class Samochod {
  public function przedstawSie() {
    echo "Jestem Samochodem";
  }
}

class Terenowy extends Samochod { // Błąd, klasa Terenowy nie może dziedziczyć po klasie Samochod

}
?>

Tym zakończę lekcję o dziedziczeniu.

Dzięki tej lekcji potrafisz:

  • stworzyć klasę rozszerzającą inną klasę
  • korzystać z pól i metod klasy bazowej
  • nadpisywać metody klasy bazowej
  • blokować nadpisywanie przy pomocy słowa final

Teraz czas na poznanie konstruktorów klas, które pomogą Ci utworzyć właściwy stan obiektu już podczas jego tworzenia. Zapraszam!

Spis lekcji w programowaniu obiektowym

Poprzednia lekcja: Modyfikatory dostępu Następna lekcja: Konstruktory