W budowie
Materiał edukacyjny · Informatyka Hermetyzacja danych, relacje między klasami i rozszerzanie funkcjonalności

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, protected i private oraz 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();
1. Co oznacza private?
2. Co oznacza protected?
3. Po co enkapsulacja?

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;
    }
}
4. Co robi getter?
5. Co powinien robić setter?
6. Dlaczego jawne gettery?

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();
7. Czym rozszerzamy klasę?
8. Co daje parent::metoda()?
9. Jaką relację opisuje dziedziczenie?

Ć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.

1. Który modyfikator dostępu w PHP ogranicza widoczność pola WYŁĄCZNIE do wnętrza klasy, w której zostało zadeklarowane (blokując dostęp nawet klasom dziedziczącym)?
2. Jaki będzie skutek wykonania poniższego kodu?
<?php
class Gracz {
    private int $punkty = 100;
}

$g = new Gracz();
echo $g->punkty;
?>
3. Głównym celem stosowania metod dostępowych (setterów) zamiast właściwości publicznych jest:
4. Które słowo kluczowe służy w PHP do wskazania klasy bazowej (rodzica), z której dziedziczy nowo definiowana klasa?
5. Jaki wynik wyświetli poniższy kod?
<?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();
?>
6. Jak w konstruktorze klasy potomnej prawidłowo wywołać konstruktor klasy nadrzędnej?
7. Jaka wartość zostanie wyświetlona po wykonaniu poniższego kodu?
<?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();
?>
8. Co wyświetli poniższy kod demonstrujący użycie słowa kluczowego 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();
?>
9. Jaki będzie skutek próby odziedziczenia po klasie oznaczonej jako final class?
<?php
final class BezpiecznyToken {
    public string $klucz = "abc123xyz";
}

class ZlamanyToken extends BezpiecznyToken {}
?>
10. Do czego służy słowo kluczowe final umieszczone przed deklaracją metody w klasie bazowej?
11. Co wydarzy się podczas uruchomienia poniższego kodu?
<?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");
?>
12. Czy klasa pochodna może zmienić widoczność przesłanianej metody z public na private?
13. Co stanie się w poniższym kodzie podczas wywołania metody 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();
?>
14. Jeśli klasa potomna definiuje własną metodę __destruct(), to czy destruktor klasy bazowej wykona się automatycznie?
15. Jaki rezultat dadzą poniższe porównania operatorem instanceof?
<?php
class Figura {}
class Kwadrat extends Figura {}

$k = new Kwadrat();
var_dump($k instanceof Kwadrat);
var_dump($k instanceof Figura);
?>
16. Co wyświetli poniższy łańcuch trzech dziedziczących klas po wywołaniu $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();
?>