Programowanie obiektowe #2 – hermetyzacja

Witajcie w kolejnym wpisie z serii o obiektowości, drodzy czytelnicy! W poprzednim poście dowiedzieliście się czym jest dziedziczenie, a także na jakich filarach opiera się programowanie obiektowe. Dzisiaj zajmiemy się kolejnym z nich – omówimy wspólnie czym jest hermetyzacja.

Hermetyzacja – czym ona jest?

Mówiąc krótko hermetyzacja, zwana też enkapsulacją, polega na ograniczaniu dla klas zewnętrznych dostępu do danych i metod tworzonej klasy. Dzięki temu zapobiegamy niekontrolowanemu działaniu programu, a także zachowujemy przejrzystą strukturę obiektów w projekcie.  Wyobraź sobie, że tworzysz bazę klientów w programie. Stworzysz najpewniej klasę Client, w której zawrzesz informacje na temat danego klienta, jak jego imię, nazwisko, wiek, zamieszkanie itd. Nie chcemy jednak, aby w trakcie działania programu jego imię, czy nazwisko zostało zmienione przez kogoś innego. Musimy więc uniemożliwić zmianę tych dwóch pól, dając jedynie dostęp do ich odczytu. Na tym właśnie polega hermetyzacja. Aby jak najściślej zachować zasady hermetyzacji, stosuje się różne praktyki:

  1. korzystanie z modyfikatorów dostępności – jak najmniej danych/metod powinno być widocznych na zewnątrz klasy
  2. Ukrywanie pól klasy i nadawanie dostępu do nich poprzez funkcje get i set
  3. Korzystanie z wartości stałych/tylko do odczytu
hermetyzacja - enkapsulacja

Hermetyzacja – modyfikatory dostępu

Modyfikatory omawialiśmy już w poprzednim wpisie, jednak warto je sobie przypomnieć:

  1. Public – metody lub pola oznaczone tym modyfikatorem są dostępne dla każdego, kto posiada instancję naszej klasy, a także dla klas dziedziczących
  2. Protected – metody lub pola oznaczone tym modyfikatorem są dostępne tylko w obrębie danej klasy i w klasach pochodnych
  3. Private – metody lub pola oznaczone tym modyfikatorem są dostępne tylko w obrębie danej klasy

Modyfikatory dostępu możemy przypisać do każdej metody, pola, a nawet konstruktorów. W języku C++ użycie modyfikatora dostępu przed nazwą klasy pozwala na określenie zakresu dziedziczenia jej właściwości i zachowania przez klasy pochodne. Więcej o tych zakresach przeczytacie w poprzednim wpisie.

Przejdźmy do prostego przykładu:

Producer.h

Producer.cpp

Stworzyliśmy przykładową klasę producenta, która zawiera informacje o jej nazwie, przechowywanych produktach, a także metody pozwalające na manipulację nimi. Zauważ, że metoda sprawdzająca produkty przed wydaniem jest oznaczona jako protected, co oznacza, że nie będzie możliwe sprawdzenie produktów poza tą klasą lub klasami pochodnymi. Pola przechowujące nazwę i wyprodukowane przedmioty oznaczyliśmy jako private, ponieważ nie chcemy, aby ktokolwiek modyfikował ich zawartość poza tą klasą. Dodatkowo, dla przykładu, stworzyliśmy również prywatną metodę sprawdzającą poprawność danego produktu – chcemy, aby była ona dostępna tylko dla bazowej metody sprawdzającej wszystkie produkty. Aby nie komplikować, za wadliwy produkt uznajemy każdy, który zawiera w swojej nazwie znaki specjalne.

Hermetyzacja – gettery/settery, czyli ustalanie dostępu do pól

Najbardziej charakterystyczną praktyką hermetyzacji jest korzystanie z tzw. getterów i seterów. Na czym one polegają? Mówiąc krótko są to specjalne metody, które dają dostęp do prywatnych pól danej klasy.

Wróćmy do przykładu naszego producenta. Powiedzmy, że chcemy, aby magazyn produktów mógł być zmieniany przez każdego, kto ma dostęp do instancji klasy producenta – może to być dostawa nowych przedmiotów lub zmiana asortymentu. Można by po prostu zmienić widoczność vectora na publiczną, jednak w praktyce korzysta się z innego rozwiązania. Dodajemy dwie publiczne metody: pierwsza zwraca wartość naszego vectora, a druga typu void pozwala na ustawienie jego wartości. Dlaczego takie rozwiązanie jest lepsze? Dzięki takiemu podejściu mamy większą kontrolę nad danymi, które ktoś wprowadza. Przykładowo przed zmianą wartości możemy od razu pozbyć się wadliwych produktów, a następnie oznaczyć produkty jako sprawdzone, dzięki czemu upewnimy sie, że wszystkie dane są poprawne. Dodatkowo chcemy, aby nazwa naszego producenta była widoczna dla każdego, ale bez możliwości jej zmiany – wtedy wystarczy stworzyć jedynie metodę zwracającą jej wartość.

Tak wygląda nasza klasa po zmianach:

 Producer.h

Producer.cpp

Hermetyzacja – stałe

Pola stałe inicjalizowane są ze słowem kluczowym const. Pola takie po inicjalizacji nie mogą już zostać w żaden sposób zmienione – ani przez klasę je zawierająca, ani przez nikogo innego. Stałe przydają się, gdy w programie często korzystamy z tej samej wartości. Dobrą praktyką jest wtedy stworzenie stałej, która zainicjalizuje dany obiekt. Dzięki temu nie musimy potem wielokrotnie wywoływać jego konstruktora w czasie działania programu. W naszym przykładzie możemy stworzyć pole zawierające zainicjalizowany regex – jego wartość z założenia ma być zawsze taka sama, więc użycie stałej jest tutaj dobrym rozwiązaniem.

Producer.cpp

Producer.h

Zbierzmy teraz wszystkie przykłady do kupy. Stworzymy 3 instancje klasy producenta, a następnie wyprodukujemy 3 serie po 5 produktów, których nazwy wybierzemy (pseudo)losowo z przygotowanej listy. W liście tej celowo zawrzemy kilka wadliwych produktów, aby sprawdzić działanie naszego programu. Następnie wydrukujemy zawartość listy produktów przed i po sprawdzeniu.

Main.cpp 

A oto część wyniku działania naszego programu:

hermetyzacja - wynik dzialania programu

Podsumowanie

Dowiedzieliście się dzisiaj czym jest hermetyzacja, a także w jaki należy ją praktykować. W następnym wpisie z serii omówimy czym jest kolejny jego filar: polimorfizm.

You Might Also Like
Dodaj komentarz

icon