Hermetyzacja

Słownik kwalifikacji INF.04 - Projektowanie, programowanie i testowanie aplikacji

Co to jest hermetyzacja?

Hermetyzacja (ang. encapsulation) to jedna z czterech podstawowych zasad programowania obiektowego. Polega na ukrywaniu wewnętrznych elementów klasy — pól i metod pomocniczych — przed bezpośrednim dostępem z zewnątrz. Dostęp do danych powinien odbywać się przez wyznaczone metody klasy (gettery, settery, metody publiczne) lub funkcje zaprzyjaźnione (friend w C++).

Infografika: hermetyzacja w programowaniu obiektowym — ukrywanie pól klasy modyfikatorami private/protected/public, dostęp przez gettery i settery oraz typowe błędy dostępu z modyfikatorem protected

W formule egzaminacyjnej CKE hermetyzacja to:

Zasada programowania obiektowego polegająca na ukrywaniu elementów klasy tak, aby były one dostępne wyłącznie dla metod tej klasy lub funkcji zaprzyjaźnionych.

Hermetyzacja to obowiązkowy element w kwalifikacji INF.04 (Projektowanie, programowanie i testowanie aplikacji). Pozostałe filary OOP omawia hasło programowanie obiektowe.

Cel hermetyzacji

Hermetyzacja:

  • chroni dane obiektu przed niekontrolowaną modyfikacją z zewnątrz,
  • ogranicza błędy wynikające z niepoprawnego użycia klasy,
  • pozwala walidować dane przy zapisie (np. nie ujemne saldo, poprawny PESEL),
  • ułatwia zmianę wewnętrznej implementacji bez wpływu na kod korzystający z klasy,
  • daje czytelniejsze API klasy — wiadomo, czego można używać z zewnątrz.

Modyfikatory dostępu

Hermetyzację realizuje się przez modyfikatory dostępu, określające, skąd można korzystać z pola lub metody:

  • private — element dostępny tylko wewnątrz danej klasy,
  • protected — element dostępny w klasie i w klasach dziedziczących,
  • public — element dostępny z zewnątrz (np. przez obj.pole).

W C++ dodatkowo istnieje pojęcie funkcji zaprzyjaźnionych (friend) — funkcji spoza klasy, które dostają dostęp do jej prywatnych elementów. Stąd egzaminacyjne sformułowanie „dostępne dla metod tej klasy lub funkcji zaprzyjaźnionych".

Dostęp przez nazwaObiektu.nazwaPola

Z poziomu głównego programu, przez odwołanie w formie nazwaObiektu.nazwaPola, dostępne są tylko pola public. Pola private i protected są z tego poziomu niewidoczne.

Modyfikator dla metody dostępnej w klasie i pochodnych

Jeżeli metoda ma być dostępna z wnętrza klasy i z klas dziedziczących, ale nie z zewnątrz (przez obj.metoda()), używa się modyfikatora protected:

protected void Dodaj() { }

Tak zadeklarowana metoda Dodaj() jest dostępna wewnątrz klasy Kalkulator oraz w klasach po niej dziedziczących — nie z głównego programu.

Przykład — klasa Konto

class Konto {
private:
    double saldo;       // ukryte pole

public:
    Konto() : saldo(0) {}

    void wplac(double kwota) {
        if (kwota > 0) {           // walidacja
            saldo += kwota;
        }
    }

    double pobierzSaldo() {
        return saldo;
    }
};

Pole saldo jest prywatne, więc nie można go bezpośrednio modyfikować z zewnątrz. Zmiana salda odbywa się tylko przez metodę wplac(), która sprawdza poprawność danych (nie wpłacisz wartości ujemnej). To klasyczny przykład hermetyzacji + walidacji.

Typowy błąd dostępu — pytanie egzaminacyjne

Pytania CKE często sprawdzają, czy uczeń rozumie, że protected nie daje dostępu z zewnątrz klasy. Przykład:

class Dokument {
    public string nazwa;
    protected string autor;
}

// w funkcji main:
Dokument doc = new Dokument();
doc.nazwa = "Raport";      // OK — public
doc.autor = "Jan Kowalski"; // BŁĄD — protected niedostępne z zewnątrz!

Źródło błędu: próba dostępu do pola autor z modyfikatorem protected spoza klasy Dokument i spoza klas pochodnych. Z main można czytać i zapisywać tylko pola public.

Aby ten kod zadziałał, trzeba:

  • albo zmienić modyfikator pola autor na public,
  • albo udostępnić zmianę przez publiczną metodę (np. ustawAutora(string)).

Hermetyzacja a dziedziczenie

Hermetyzacja działa również w hierarchii klas. Pola private klasy bazowej nie są bezpośrednio widoczne w klasie pochodnej, mimo dziedziczenia.

public class Customer {
    public string Name;     // widoczne w pochodnych
    protected int Id;       // widoczne w pochodnych
    private int Age;        // NIE widoczne w pochodnych
}

public class GoldCustomer : Customer { ... }

W metodach klasy GoldCustomer widoczne są tylko Name i Id. Pole Age jest private, więc — mimo dziedziczenia — jest niedostępne.

Więcej o samym dziedziczeniu znajdziesz w haśle klasa i obiekt.

Hermetyzacja + static (pole prywatne statyczne)

Modyfikatory hermetyzacji łączą się z innymi (np. static):

class MojaKlasa {
    private static int licznik;
}

Z definicji private static int licznik wynika, że pole:

  • jest wspólne dla wszystkich obiektów klasy (static),
  • jest dostępne tylko wewnątrz klasy (private).

To typowe rozwiązanie dla licznika utworzonych instancji — dane wspólne dla całej klasy, ale ukryte przed światem zewnętrznym.

Gettery i settery — sposób kontrolowanego dostępu

Ponieważ pól prywatnych nie można czytać ani modyfikować z zewnątrz, udostępnia się je przez publiczne metody nazywane getterami (czytanie) i setterami (zapis):

public class Osoba {
    private int wiek;

    public int getWiek() {            // getter
        return wiek;
    }

    public void setWiek(int w) {      // setter z walidacją
        if (w >= 0 && w < 150) {
            this.wiek = w;
        }
    }
}

To pozwala na kontrolowany dostęp — czytanie zawsze działa, ale zapis można obwarować dowolnymi warunkami. W C# i Pythonie można dodatkowo używać właściwości (properties), które wyglądają z zewnątrz jak pola, a w środku są metodami.

Hermetyzacja jako część podejścia obiektowego

Obiektowe podejście do rozwiązywania problemów obejmuje między innymi:

  • modelowanie problemu za pomocą klas i obiektów,
  • stosowanie czterech filarów: hermetyzacji, dziedziczenia, polimorfizmu i abstrakcji,
  • grupowanie danych i operacji w logiczne jednostki.

Hermetyzacja nie istnieje w oderwaniu od pozostałych filarów — działa razem z dziedziczeniem (np. protected), z polimorfizmem i z abstrakcją. Pełen kontekst opisuje hasło programowanie obiektowe.

Częste pomyłki — nie myl tego!

  • hermetyzacja ≠ dziedziczenie ≠ polimorfizm — to trzy różne filary OOP. Hermetyzacja to ukrywanie, dziedziczenie to rozszerzanie klasy, polimorfizm to wspólny interfejs dla różnych klas.
  • privateprotectedprivate nie jest widoczne w klasie pochodnej, protected jest. Z zewnątrz żadne z nich nie jest dostępne przez obj.pole.
  • protected nie daje dostępu z main — to częsta pułapka egzaminacyjna. protected to „pochodne + ja", nie „wszyscy".
  • hermetyzacja to nie zakaz dostępu w ogóle — to kontrolowany dostęp przez metody publiczne.
  • getter/setter ≠ obejście hermetyzacji — to jej narzędzie, nie naruszenie. Dobry setter waliduje dane.
  • friend istnieje w C++, ale nie w Javie ani C# — w pozostałych językach do podobnych celów służą metody / klasy zagnieżdżone albo właściwości.
  • public string autor w klasie ≠ globalna zmienna — to wciąż pole obiektu, tylko bez ochrony przed bezpośrednią zmianą.

Najważniejsze do zapamiętania

Hermetyzacja to ukrywanie elementów klasy tak, aby były dostępne wyłącznie dla metod tej klasy lub funkcji zaprzyjaźnionych. Realizuje się ją głównie przez modyfikator private (i ewentualnie protected dla klas pochodnych). Z poziomu głównego programu, przez obj.pole, dostępne są tylko pola public. Próba modyfikacji pola protected z main to typowy błąd rozpoznawany na egzaminie. Kontrolowany dostęp do pól prywatnych zapewniają gettery i settery.