CZĘŚĆ 24 · PROGRAMOWANIE OBIEKTOWE (OOP)
Polimorfizm, interfejsy, klasy abstrakcyjne i elementy statyczne
PPrezentacja dla nauczyciela
W rzeczywistych projektach webowych i frameworkach (np. Laravel, Symfony) rzadko pracujemy z pojedynczymi, odizolowanymi klasami. Prawdziwa siła obiektowości tkwi w projektowaniu wymiennych komponentów i definiowaniu kontraktów. Dzięki nim kod potrafi obsłużyć różne implementacje (np. bramkę PayU, Przelewy24 lub PayPal) za pomocą jednego, spójnego interfejsu — to zjawisko nazywamy polimorfizmem (wielopostaciowością).
W tej części zgłębisz zaawansowane mechanizmy OOP stosowane w profesjonalnych aplikacjach PHP oraz w pytaniach na egzaminach zawodowych i rozmowach rekrutacyjnych:
- Klasy i metody abstrakcyjne (
abstract): Szablony, których nie można instancjonować bezpośrednio, służące jako niepełna baza wymuszająca zdefiniowanie konkretnych metod w klasach pochodnych. - Interfejsy (
interfaceorazimplements): Czyste kontrakty określające, jakie metody klasa musi udostępniać, bez narzucania sposobu ich wykonania. - Wielokrotna implementacja: W przeciwieństwie do dziedziczenia klas, jedna klasa w PHP może implementować wiele niezależnych interfejsów jednocześnie.
- Polimorfizm w praktyce: Jak pisać funkcje operujące na typach interfejsowych zamiast konkretnych klasach, eliminując długie łańcuchy
if/elseczyswitch. - Pola i metody statyczne (
static): Składowe należące do samej klasy, a nie do jej pojedynczych instancji. - Operator zakresu
::(Paamayim Nekudotayim): Różnica międzyself::a$this->oraz dlaczego w metodzie statycznej nie istnieje zmienna$this. - Stałe klasowe (
const): Niezmienne wartości powiązane z klasą i ich bezpieczny odczyt. - Obiektowa obsługa błędów (Wyjątki): Rzucanie (
throw new Exception()) oraz przechwytywanie wyjątków w blokachtry-catch-finallyz analizą obiektów błędów.
Sprawdź swoją wiedzę, rozwiązując zestaw 16 przekrojowych pytań quizowych na dole strony.
Klasy abstrakcyjne
Klasy abstrakcyjnej nie można utworzyć przez new — służy jako wspólna baza. Może zawierać zwykłe metody oraz metody abstrakcyjne, czyli same nagłówki bez treści.
Klasa potomna musi zaimplementować wszystkie metody abstrakcyjne. To wymusza spójny interfejs: każda figura ma na przykład metodę pole().
Klasa abstrakcyjna różni się od interfejsu tym, że może mieć działający kod i właściwości. Interfejs opisuje wyłącznie zestaw metod.
abstract class Figura {
abstract public function pole();
public function opis() {
return 'Pole figury: ' . round($this->pole(), 2);
}
}
class Kwadrat extends Figura {
public function __construct(private float $bok) {}
public function pole() {
return $this->bok ** 2;
}
}
echo (new Kwadrat(3))->opis();
Interfejsy i polimorfizm
Interfejs deklaruje metody bez implementacji, a klasa przyjmuje go słowem implements. Jedna klasa może implementować kilka interfejsów, bo w PHP dziedziczyć można tylko po jednej klasie.
Polimorfizm polega na tym, że różne klasy mają metody o tej samej nazwie i można je wywoływać w ten sam sposób. Pętla po figury może liczyć pole niezależnie od konkretnej klasy.
Interfejs jest umową: kod, który go używa, nie musi znać szczegółów implementacji. Wystarczy, że obiekt ma wymagane metody.
interface Figura {
public function pole(): float;
}
class Kolo implements Figura {
public function __construct(private float $r) {}
public function pole(): float { return pi() * $this->r ** 2; }
}
class Prostokat implements Figura {
public function __construct(private float $a, private float $b) {}
public function pole(): float { return $this->a * $this->b; }
}
foreach ([new Kolo(1), new Prostokat(2, 3)] as $f) {
echo round($f->pole(), 2) . ' ';
}
Składowe statyczne i stałe klasowe
Składowa statyczna należy do klasy, a nie do obiektu — istnieje w jednym egzemplarzu. Wywołujemy ją przez self:: wewnątrz klasy albo Klasa:: na zewnątrz.
static przydaje się do liczników i funkcji pomocniczych, które nie potrzebują danych obiektu. Do obiektu odwołujemy się wtedy przez $this, a do statycznej przez self.
const definiuje stałą klasową — wartość niezmienną, czytaną tą samą składnią. Dobrze opisuje np. stawkę VAT obowiązującą w całej klasie.
class Licznik {
private static int $ile = 0;
public const WERSJA = '1.0';
public static function dodaj(): void {
self::$ile++;
}
public static function wartosc(): int {
return self::$ile;
}
}
Licznik::dodaj();
Licznik::dodaj();
echo Licznik::wartosc() . ' / ' . Licznik::WERSJA;
Ćwiczenia — Quiz wiedzy o zaawansowanym OOP w PHP (16 pytań)
Przetestuj swoje zrozumienie klas abstrakcyjnych, interfejsów, polimorfizmu, elementów statycznych i obsługi wyjątków. Zaznacz prawidłowe odpowiedzi w poniższych 16 pytaniach, a następnie sprawdź swój wynik na dole strony.
abstract class?
<?php
abstract class Pojazd {
public int $kola = 4;
}
$p = new Pojazd();
?>
abstract function) w PHP jest prawdziwe?
<?php
interface PowitanieInterface {
public function pobierzTekst(): string;
}
class Dziennik implements PowitanieInterface {
public function pobierzTekst(): string { return "Dzień dobry"; }
}
class Noc implements PowitanieInterface {
public function pobierzTekst(): string { return "Dobry wieczór"; }
}
function wyswietl(PowitanieInterface $p): void {
echo $p->pobierzTekst();
}
?>
<?php
class Licznik {
public static int $ile = 0;
public function zwieksz(): void {
self::$ile++;
}
}
$a = new Licznik();
$a->zwieksz();
$b = new Licznik();
$b->zwieksz();
echo Licznik::$ile;
?>
pomnoz zdefiniowaną w klasie Kalkulator bez tworzenia instancji?
$this wewnątrz metody zadeklarowanej jako public static function?
<?php
class Narzedzia {
public string $wersja = "1.0";
public static function sprawdz(): void {
echo $this->wersja;
}
}
Narzedzia::sprawdz();
?>
<?php
class Konfiguracja {
public const WERSJA = '2.4.0';
}
?>
<?php
class Sesja {
public static int $licznik = 0;
public function __construct() {
self::$licznik++;
}
}
$s1 = new Sesja();
$s2 = new Sesja();
new Sesja();
echo Sesja::$licznik;
?>
try-catch?
<?php
try {
throw new Exception("Brak połączenia z bazą!");
echo "Sukces";
} catch (Exception $e) {
echo "Złapano: " . $e->getMessage();
}
?>
Exception oraz Error) w nowoczesnym PHP?
try-catch-finally wykonywany jest kod zawarty w bloku finally?
<?php
interface Dokument {
public function pobierzTresc(): string;
}
class Drukarka {
public static string $status = "OK";
public static function drukuj(Dokument $d): void {
echo $d->pobierzTresc() . " [" . self::$status . "]";
}
}
class Notatka implements Dokument {
public function __construct(private string $tekst) {}
public function pobierzTresc(): string {
return "Notatka: " . $this->tekst;
}
}
$n = new Notatka("Zakupy");
Drukarka::drukuj($n);
?>