Przeczytaj
Programowanie obiektowe
W programowaniu strukturalnym zmienne i funkcje na nich operujące traktowane są oddzielnie – programista musi pamiętać, które dane powinny zostać użyte z którą funkcją. W przypadku programowania obiektowego dane i operacje są ze sobą bezpośrednio powiązane. Ten paradygmat ma za zadanie ułatwić pisanie i sprawić, by kod był bardziej czytelny oraz łatwiejszy w modyfikacji. Pozwala on na traktowanie dużych fragmentów kodu składających się z wielu funkcji i zmiennych jako tzw. czarnych skrzynek – w praktyce oznacza to, że po napisaniu danego fragmentu kodu operujemy tylko na najbardziej zewnętrznych jego warstwach, co ułatwia zrozumienie koncepcji programu, ponieważ nie musimy wdawać się w detale. Przykładowo, znacznie łatwiej myśleć o rowerach niż osobno o kołach, ramach, oponach oraz wszystkich funkcjach, jak hamowanie, zmiana biegów czy pompowanie opon. Takie pominięcie szczegółów i skupienie się na ogółach nazywane jest w programowaniu abstrakcją.
Pierwsza koncepcja, by stworzyć taki sposób pisania kodu, powstała w języku Simula 67 – języku zaprojektowanym do tworzenia symulacji. Jego autorzy pracowali nad opisem symulowanych procesów oraz interakcji, zachodzących pomiędzy różnymi statkami. W celu uwzględnienia wszystkich zależności, występujących między jednostkami, dokonano pogrupowania na klasy. I to one miały odpowiadać za określanie swoich parametrów.
Klasa – przykłady z życia
Klasa to definicja cech obiektów. Przez cechy rozumiemy pola (czyli zmienne zawarte w klasach) oraz metody (czyli akcje, jakie można wykonać na obiektach danej klasy). Najprościej mówiąc, zmienne w klasach to pola, a funkcje w klasach to metody.
Przykładem klasy może być samochód. Polami takiej klasy będą np.:
liczba miejsc,
liczba pasażerów,
rodzaj silnika,
kolor,
maksymalna prędkość,
obecna prędkość.
Metody w klasie zazwyczaj (choć nie zawsze) operują na zawartych w niej polach. Metodami w klasie samochodu mogą zatem być:
przyśpieszenie, które zwiększa obecną prędkość, o ile nie zostanie przekroczona prędkość maksymalna oraz zwalnianie, które zmniejsza prędkość,
wsiadanie oraz wysiadanie pasażerów, które w odpowiedni sposób zmienia wartość pola przechowującego liczbę pasażerów, o ile nie przekroczy ono liczby miejsc.
Innymi przykładami klas mogą być na przykład uczniowie w szkole, postacie w grze komputerowej czy też paczki w firmie kurierskiej.
Obiekt – przykłady z życia
Obiektem nazywamy instancję klasy, czyli konkretną „rzecz” reprezentującą daną klasę. Dla omawianego wcześniej przykładu klasy samochodu, obiektem tej klasy byłby jeden konkretny samochód. Taki obiekt miałby wypełnione wszystkie pola i według naszej definicji byłby w pełni funkcjonalnym samochodem.
Możemy zatem stworzyć obiekt klasy samochód, którego pola będą wypełnione następującymi wartościami:
liczba miejsc – 4,
liczba pasażerów – 0,
rodzaj silnika – elektryczny,
kolor – srebrny,
maksymalna prędkość – 130 kilometrów na godzinę,
obecna prędkość – 0 kilometrów na godzinę.
Takie wartości mogą reprezentować zaparkowane elektryczne auto. Warto ponownie zwrócić uwagę na użytą tu abstrakcję - na potrzeby naszej klasy musieliśmy podać tylko te wartości, nie było tu natomiast konieczności podawania informacji o szczegółach konstrukcyjnych, właściwościach aerodynamicznych czy pojemności. Niemniej w rozbudowanej symulacji – choćby takiej, jaka doprowadziła do powstania koncepcji programowania obiektowego – pól byłoby znacznie więcej.
Nic nie stoi na przeszkodzie, żebyśmy stworzyli kolejne obiekty klasy samochodu. Będą to dwa oddzielne byty w pamięci komputera, które mogą zmieniać się niezależnie od siebie.
Dziedziczenie – czym jest i jak działa?
Dziedziczenie to mechanizm, dzięki któremu jest możliwe dzielenie funkcjonalności między klasami. Jeśli klasa dziedziczy po innej klasie, to znaczy, że poza swoimi polami i metodami ma też te, które są zapisane w klasie, z której dziedziczy. Klasa dziedzicząca nazywana jest klasą pochodną, a klasa, z której jest dziedziczone – klasą bazową (lub nadrzędną).
W kontekście dziedziczenia, klasy możemy podzielić na dwa rodzaje:
klasa właściwa – klasa, której obiekt można utworzyć i na nim działać, czasami nazywana klasą konkretną;
klasa abstrakcyjna – klasa, której obiektu nie można utworzyć.
Klasy abstrakcyjne stosowane są tylko wtedy, kiedy wykorzystujemy dziedziczeniedziedziczenie. Klasa abstrakcyjna stanowi pewną podstawę, którą podczas dziedziczenia należy rozbudować tak, aby powstała klasa spełniała swoje zadanie. Dzięki klasom abstrakcyjnym możemy w łatwy sposób uchronić się przed stworzeniem bezsensownego obiektu - przykładowo, klasa Pojazd powinna być klasą abstrakcyjną. Wszystkie cechy wspólne, jakie posiadają pojazdy, nie są wystarczające do stworzenia jednego, konkretnego pojazdu, bez doprecyzowania tych cech w klasie pochodnej.
Klasą bazową może być zarówno klasa właściwa, jak i klasa abstrakcyjna. Z jednej klasy bazowej może dziedziczyć nieskończenie wiele klas pochodnych.
Języki takie jak Java, C#, Go wspierają koncepcję zwaną interfejsami. Działają one bardzo podobnie do klas abstrakcyjnych, jednak nie pozwalają m.in. na tworzenie pól.
W niektórych językach programowania możliwe jest wykorzystanie ograniczeń widoczności pól lub metod podczas dziedziczenia. Modyfikatory dostępu do pól i metod dzielą się na:
publiczne (public) – dany element można wywołać w każdym miejscu; może być wykorzystywany poza wnętrzem klasy, w której został zadeklarowany, czy też w jednej z jej klas pochodnych,
prywatne (private) – dany element można wykorzystać jedynie we wnętrzu klasy, w której został utworzony; nie jest możliwe wykorzystywanie go w klasach pochodnych,
chronione (protected) – dany element można wywołać tylko w klasie, w której został zadeklarowany, oraz w każdej z jej klas pochodnych.
W zależności od języka, możliwe może być dziedziczenie z kilku klas naraz, czyli wielokrotne. Umożliwiają to np. języki C++ i Python, jednak Java oraz C# wymagają rozwiązania problemu wielokrotnego dziedziczenia w inny sposób. Każda klasa pochodna może stać się klasą bazową – to tzw. dziedziczenie wielopokoleniowe lub wielopoziomowe.
Aby zrozumieć wykorzystanie dziedziczenia w praktyce, rozważmy sytuację, w której chcemy napisać elektroniczny dziennik. Załóżmy, że mamy klasę osoby, która posiada pola opisujące imię, nazwisko oraz datę urodzenia. Dodajmy również metodę, która zwraca łańcuch znaków będący krótkim podsumowaniem tych informacji.
Chcąc dodać klasę oznaczającą ucznia, gdyby dziedziczenie nie było możliwe, musielibyśmy skopiować kod oraz wpisać go ponownie do drugiej klasy. Dziedziczenie umożliwia jednak sprawienie, że uczeń jest klasą pochodną od osoby, przez co będzie posiadał jej cechy. Możemy również dodać inne pola, takie jak średnia ocen czy numer w dzienniku.
Takie podejście umożliwia stworzenie np. klasy reprezentującej nauczyciela, która będzie korzystała z tej samej funkcjonalności z klasy osoby. Jeżeli klasa bazowa zostanie zmieniona, zmiana ta będzie dotyczyła również wszystkich klas pochodnych.
Słownik
technika polegająca na wyizolowaniu elementów kluczowych do danego zadania, bez skupiania się na szczegółach
mechanizm, dzięki któremu jest możliwe wykorzystanie cech danej klasy do stworzenia klasy bardziej wyspecjalizowanej
obiekt stworzony z definicji klasy
funkcje realizowane na obiektach danej klasy
klasa, której obiektu nie możemy stworzyć; może być wykorzystywana jedynie jako klasa bazowa przy dziedziczeniu
klasa, z której następuje dziedziczenie
klasa, która dziedziczy
klasa, która pozwala na tworzenie obiektów