CZĘŚĆ 23 · PROGRAMOWANIE OBIEKTOWE (OOP)
Modyfikatory dostępu, enkapsulacja i dziedziczenie
PPrezentacja dla nauczyciela
Gdy wszystkie właściwości klasy są publiczne, każdy fragment kodu z zewnątrz może bezkarnie zmienić ich wartości — np. ustawić ujemną cenę towaru, zresetować saldo konta czy wpisać błędny adres e-mail. Enkapsulacja (hermetyzacja) chroni integralność danych, ukrywając wewnętrzną implementację obiektu i udostępniając jedynie bezpieczny, kontrolowany interfejs.
Drugim filarem OOP jest dziedziczenie — mechanizm pozwalający tworzyć nowe klasy w oparciu o już istniejące, przejmując ich cechy i zachowania bez powielania kodu (zasada DRY — Don't Repeat Yourself). W tej części opanujesz:
- Trzy poziomy widoczności: Różnice między
public,protectediprivateoraz kiedy stosować każdy z nich. - Akcesory (gettery i settery): Jak kontrolować odczyt i zapis danych oraz przeprowadzać walidację przed zmianą stanu.
- Właściwości niemutowalne (
readonly): Jak zabezpieczyć pola przed powtórnym nadpisaniem po zainicjalizowaniu w konstruktorze (PHP 8.1+). - Mechanizm dziedziczenia (
extends): Relacja rodzic-dziecko (klasa bazowa i pochodna) oraz ponowne wykorzystanie logiki. - Konstruktor w klasie potomnej i
parent::__construct():Jak poprawnie zainicjalizować właściwości odziedziczone po klasie nadrzędnej. - Przesłanianie metod (Overriding): Jak dostosować zachowanie metody bazowej do specyfiki klasy potomnej i jak odwołać się do oryginału przez
parent::metoda(). - Blokowanie dziedziczenia (
final): Jak zabezpieczyć klasę lub metodę przed modyfikacją przez inne klasy.
Przeanalizuj poniższe podsumowanie i przykład, a następnie rozwiąż 16 pytań quizowych, które przetestują Twoje zrozumienie reguł dostępu i hierarchii klas.
Modyfikatory dostępu
public oznacza dostęp z każdego miejsca, protected — z klasy i klas potomnych, a private — tylko z wnętrza tej klasy. Domyślnie właściwość jest publiczna, ale lepiej pisać to jawnie.
Ukrywanie danych to enkapsulacja: świat zewnętrzny nie zmienia właściwości bez kontroli. Dzięki temu klasa pilnuje poprawności swoich danych.
Metody zwykle zostają publiczne, a właściwości prywatne lub chronione. To najczęstszy układ w kodzie obiektowym.
class Konto {
private $saldo = 0;
public function wplac($kwota) {
if ($kwota > 0) {
$this->saldo += $kwota;
}
}
public function saldo() {
return $this->saldo;
}
}
$k = new Konto();
$k->wplac(100);
echo $k->saldo();
Gettery i settery
Gettery zwracają wartość właściwości, a settery ją ustawiają po sprawdzeniu poprawności. Dzięki temu można zmienić wewnętrzną reprezentację bez psucia kodu użytkowników klasy.
Setter nie powinien bezwarunkowo przyjmować wszystkiego — sprawdza zakres, typ i sens wartości. W razie błędu może zgłosić wyjątek albo pominąć zmianę.
W PHP dostępne są też metody magiczne __get() i __set(), ale jawne gettery są czytelniejsze i łatwiejsze w testach.
class Ocena {
private $wartosc = 1;
public function ustaw($wartosc) {
$wartosc = (int) $wartosc;
if ($wartosc >= 1 && $wartosc <= 6) {
$this->wartosc = $wartosc;
}
}
public function wartosc() {
return $this->wartosc;
}
}
Dziedziczenie klas
Klasa potomna rozszerza bazową słowem extends i dziedziczy jej publiczne oraz chronione składowe. Dzięki temu nie trzeba pisać wspólnego kodu drugi raz.
Metodę z klasy bazowej można wywołać przez parent::metoda(), np. w konstruktorze potomnym. To standardowy sposób rozszerzenia zachowania bez kopiowania.
Dziedziczenie opisuje relację „jest”: Nauczyciel jest Osobą. Jeśli relacji nie ma, lepiej użyć kompozycji, czyli pola z obiektem innej klasy.
class Osoba {
protected $imie;
public function __construct($imie) {
$this->imie = $imie;
}
public function przedstaw() {
return 'Jestem ' . $this->imie;
}
}
class Nauczyciel extends Osoba {
public function przedstaw() {
return parent::przedstaw() . ' i ucze.';
}
}
echo (new Nauczyciel('Pani Anna'))->przedstaw();
Ćwiczenia — Quiz wiedzy o enkapsulacji i dziedziczeniu (16 pytań)
Przetestuj swoje zrozumienie mechanizmów ochrony danych i relacji między klasami. Zaznacz prawidłowe odpowiedzi w poniższych 16 pytaniach i sprawdź swój wynik na dole strony.
<?php
class Gracz {
private int $punkty = 100;
}
$g = new Gracz();
echo $g->punkty;
?>
<?php
class Zwierze {
protected string $gatunek = 'Ssaki';
}
class Pies extends Zwierze {
public function pobierzDane(): string {
return "Gatunek: " . $this->gatunek;
}
}
$azor = new Pies();
echo $azor->pobierzDane();
?>
<?php
class Pracownik {
public function obliczPremie(): int {
return 500;
}
}
class Menadzer extends Pracownik {
public function obliczPremie(): int {
return 1500;
}
}
$m = new Menadzer();
echo $m->obliczPremie();
?>
parent::?
<?php
class Czlowiek {
public function powitanie(): string {
return "Cześć!";
}
}
class Pracownik extends Czlowiek {
public function powitanie(): string {
return parent::powitanie() . " Jestem pracownikiem.";
}
}
$p = new Pracownik();
echo $p->powitanie();
?>
final class?
<?php
final class BezpiecznyToken {
public string $klucz = "abc123xyz";
}
class ZlamanyToken extends BezpiecznyToken {}
?>
final umieszczone przed deklaracją metody w klasie bazowej?
<?php
class Faktura {
public readonly string $numer;
public function __construct(string $numer) {
$this->numer = $numer;
}
public function zmienNumer(string $nowy): void {
$this->numer = $nowy;
}
}
$f = new Faktura("FV/2026/01");
$f->zmienNumer("FV/2026/99");
?>
public na private?
test()?
<?php
class Baza {
private function sekret(): string {
return "1234";
}
}
class Rozszerzenie extends Baza {
public function test(): string {
return $this->sekret();
}
}
$r = new Rozszerzenie();
echo $r->test();
?>
__destruct(), to czy destruktor klasy bazowej wykona się automatycznie?
instanceof?
<?php
class Figura {}
class Kwadrat extends Figura {}
$k = new Kwadrat();
var_dump($k instanceof Kwadrat);
var_dump($k instanceof Figura);
?>
$c->start()?
<?php
class PoziomA {
public function start(): string {
return "A";
}
}
class PoziomB extends PoziomA {
public function start(): string {
return parent::start() . "B";
}
}
class PoziomC extends PoziomB {
public function start(): string {
return parent::start() . "C";
}
}
$c = new PoziomC();
echo $c->start();
?>