Programowanie obiektowe #4 – abstrakcja

Cześć! W dzisiejszym wpisie kontynuujemy kurs programowania obiektowego. W poprzednich postach poznaliście jego podstawy, a także 3 najważniejsze filary. Dzisiaj omówimy ostatni z nich: abstrakcję. Jeżeli przegapiliście poprzednie wpisy z serii, koniecznie nadróbcie zaległości:

  1. Wprowadzenie
  2. Dziedziczenie
  3. Hermetyzacja
  4. Polimorfizm

Czym jest abstrakcja?

Abstrakcja polega na ukrywaniu wszelkich pól i metod tak, aby jak najmniejsza ich ilość była widoczna dla posiadających instancje tej klasy. Mówiąc prościej: korzystającego z naszej klasy powinien interesować tylko wynik wywołania konkretnej metody – reszta szczegółów jest dla niego nieistotna. Dzięki abstrakcji możemy zachować przejrzystość kodu, jednocześnie umożliwiając rozbudowę klasy o dodatkowe elementy, bez konieczności zmian wielu innych właściwości klasy.

Aby lepiej zobrazować czym jest abstrakcja weźmy prosty przykład z życia. Większość z nas ma wiele różnych urządzeń cyfrowych: smartfon, komputer, laptop itp. W przypadku gdy jedno z tych urządzeń się popsuje, szukamy odpowiedniego serwisu, który naprawi nasze urządzenie. Z racji, iż nie jesteśmy specami w sprawach sprzętowych, to nie interesuje nas w jaki sposób zostanie to zrobione, ale oczekujemy, że serwis odda naprawiony, w pełni sprawny sprzęt. Mało tego: każdy z serwisów może mieć różne sposoby naprawy, jednak od każdego z nich oczekujemy tego samego rezultatu. Na tym właśnie polega abstrakcja – znamy funkcje danej klasy, parametry jakie przyjmuje i typ jaki zwraca, ale w ogóle nie obchodzi nas to, co dzieje się wewnątrz metody.

Warstwy abstrakcji

Abstrakcję możemy podzielić na nieskończenie wiele warstw. Weźmy na przykład serwis internetowy – w naszej warstwie abstrakcji jest to witryna internetowa, która po wpisaniu danych daje odpowiednie wyniki – nie wiemy co się dzieję w warstwach niżej. Warstwą serwisu internetowego jest cała implementacja backendu i frontendu. Warstwą niższą dla serwisu internetowego jest kod w określonym języku programowania. Idąc dalej doszlibyśmy w końcu do warstwy języka maszynowego, prądu elektrycznego, warstwy atomowej itd. Obiekt z danej warstwy korzystając z metod z niższej warstwy abstrakcji nie ma pojęcia ile działań, ale też ile warstw kryje się pod danym wywołaniem – interesuje go tylko wynik.


Prosty przykład ilustrujący kolejne warstwy abstrakcji

Klasy abstrakcyjne i metody wirtualne

Klasy abstrakcyjne i metody wirtualne omawialiśmy już w poprzednim wpisie. Przypomnijmy sobie jednak najważniejsze zagadnienia:

  • Klasy abstrakcyjne to klasy, których instancji nie da się stworzyć. Są to klasy, które definiują określone zachowania i właściwości obiektu, jednak nie posiadają ich implementacji. Klasy takie narzucają te implementacje klasom pochodnym, dzięki czemu możemy zdefiniować zachowanie wspólne dla wielu różnych klas. Klasę możemy uznać za abstrakcyjną, jeżeli posiada co najmniej jedną metodę czysto wirtualną.
  • Metody czysto wirtualne to metody, które posiadają jedynie nagłówek, bez implementacji. To właśnie dlatego nie da się stworzyć instancji klas abstrakcyjnych – są one niekompletne. Metody czysto wirtualne muszą być nadpisane w klasach pochodnych, w przeciwnym wypadku klasa pochodna również będzie klasą abstrakcyjną.
  • Metoda wirtualna to metoda, której implementacja znajduje się w klasie bazowej. Jej nadpisanie jest możliwe, ale nie jest w tym przypadku wymagane. Wywołanie metody wirtualnej skutkuje wywołaniem ostatniego nadpisania tej metody. Jeżeli ta metoda nie została nadpisana w drzewie dziedziczenia, to zostanie wywołana implementacja z klasy bazowej.

Znając podstawy abstrakcji możemy przejść do prostego przykładu:

Abstrakcja – przykład C++

Stwórzmy prostą, bazową klasę żołnierza. Żołnierz ten będzie posiadać imię, oraz wiek. Każdy z żołnierzy musisz również co jakiś czas zdawać status przełożonemu, więc stworzymy metodę czysto wirtualną. Tak prezentuje się klasa żołnierza:

Soldier.h

Soldier.cpp

Mając bazową klasę, spróbujmy stworzyć jej instancję w głównym pliku:

Main.cpp

Niestety, ale taki kod się nie skompiluje – jak już mówiliśmy, nie można stworzyć instancji obiektu klasy abstrakcyjnej. Musimy więc skorzystać z klasy pochodnej. Stworzymy w tym celu prostą klasę snajpera, która zaimplementuje metodę czysto wirtualną, a także nadpisze jedną z metod wirtualnych. Praca snajpera jest bardzo stresująca, dlatego przyjmijmy, że snajperzy wyglądają o 10 lat starzej, niż faktycznie mają. Wywołamy w tym celu metodę z klasy bazowej, a następnie zmodyfikujemy lekko jej wynik:

Sniper.h

Sniper.cpp

Mamy pochodną naszej klasy abstrakcyjnej – możemy teraz zobaczyć jak sprawuje się w akcji:

Mała uwaga: abstrakcja jest ściśle powiązana z polimorfizmem. Z tego też powodu, pomimo że nie można stworzyć instancji klasy abstrakcyjnej, to możliwe jest stworzenie wskaźnika lub referencji na daną klasę – pod taką zmienną może ukrywać się instancja dowolnej klasy pochodnej. Dzięki takiemu mechanizmowi możemy wywoływać metody z klasy, nie znając do końca jej konkretnego typu:

Podsumowanie

W dzisiejszej lekcji dowiedzieliście się czym jest abstrakcja. Jako, iż omówiliśmy dzisiaj ostatni z filarów programowania obiektowego, to od następnej lekcji zanurzymy się nieco głębiej w tajniki obiektowości: dowiecie się czym dokładnie są konstruktory i destruktory. Jeżeli chesz być na bieżąco, zapisz się do naszej listy mailingowej klikając w kopertę z menu po prawej.

You Might Also Like
Dodaj komentarz

icon