OOP
Adam Boduch
Programy rozrastają się coraz bardziej i bardziej. Tak samo jak kiedyś nie wystarczała idea programowania proceduralnego, teraz nie wystarcza już programowanie strukturalne.
Koncepcja programowania obiektowego pojawiła się już w latach 60. za sprawą języka Simula 67, zaprojektowanego przez naukowców z Oslo w celu przeprowadzania symulacji zachowania się statków. Jednakże idea programowania obiektowego swoją popularyzację zawdzięcza językowi SmallTalk. Połowa lat 80. to czas, kiedy programowanie obiektowe stało się dominującą techniką ? głównie za sprawą C++. Wtedy to też w wielu innych językach pojawiła się możliwość tworzenia obiektów.
Można powiedzieć, że klasa jest rodzajem zbioru, pewnym elementem programu, który wykonuje jakieś zadania. Klasa zawiera metody (procedury i funkcje), współdziałające ze sobą w celu wykonania jakiegoś zadania. Programowanie obiektowe przyczyniło się do tego, że takie moduły jak klasy mogą być wykorzystywane w wielu innych projektach ? ułatwia to jeszcze bardziej zarządzanie i konserwację kodu.
Załóżmy, że napisano klasę do obsługi poczty (wysyłanie i odbieranie). Klasa może zawierać procedury Connect
(połącz), SendMail
(wyślij e-mail), k(rozłącz). Z kolei procedura Connect może wywoływać inną, np. Error
(która też jest procedurą znajdującą się w klasie), wyświetlającą komunikat o błędzie w razie niepowodzenia i zapisującą odpowiednią informację w dzienniku programu (czyli, inaczej mówiąc, w logach ? plikach z rozszerzeniem *.log). Teraz taką klasę można wykorzystać w wielu swoich aplikacjach ? wystarczy skopiować fragment kodu i już gotowa jest obsługa błędów, łączenie itp. Taką klasę można udostępnić innym użytkownikom lub swoim współpracownikom. Inny użytkownik nie musi wiedzieć, jak działa klasa ? dla niego jest ważne jej działanie (np. wysyłanie e-maili). Użytkownik musi jedynie wiedzieć, że istnieje metoda Connect, która połączy go z danym serwerem oraz musi mieć świadomość obecności kilku innych procedur. To wszystko ? nie interesuje go obsługa błędów ? nie musi nawet zdawać sobie sprawy z jej istnienia.
Można by oczywiście utworzyć nowy moduł, a w nim umieścić także procedury Connect
, SendMail
oraz Disconnect
, Error
i resztę potrzebnego kodu. Jednak w takim przypadku metody i zmienne (zmienne także mogą być elementami danej klasy) nie oddziałują na siebie w takim stopniu. Przykładowo, użytkownik korzystający z takiego kodu będzie miał dostęp do tych zmiennych, do których nie powinien. Będzie mógł też wywołać swobodnie procedurę Error
? a nie powinien, bo może to spowodować niepożądane skutki. Dzięki klasom można sprawić, iż taka procedura Error nie będzie dostępna poza klasą. Jej elementy (zmienne) też będą nie do odczytania przez przyszłego użytkownika.
1 Klasy
2 Składnia klasy
3 Do czego służą klasy?
4 Hermetyzacja
5 Dziedziczenie
6 Polimorfizm
7 Instancja klasy
8 Konstruktor
9 Destruktor
9.1 Destruktory w .NET
10 Pola
11 Metody
12 Właściwości
Klasy
Klasy stanowią pewien element, zbiór procedur i funkcji. Jest to specyficzna część języka, obecna w większości języków programowania wysokiego poziomu. Obecnie idea programowania obiektowego jest bardzo popularna, a właśnie klasy są kluczowym elementem owej idei.
Cała biblioteka VCL.NET jest oparta na klasach wzajemnie czerpiących z siebie potrzebne funkcje i właściwości. Również platforma .NET dostarcza szereg klas (w tym klas związanych z Windows Forms).
Ważne jest zrozumienie, na czym polega programowanie obiektowe, gdyż na pewno zetkniesz się z tym pojęciem, jeśli tylko zechce poszerzać swoje umiejętności programistyczne w językach takich jak C# lub Java, gdzie klasy stanowią podstawę projektowania aplikacji.
Składnia klasy
W języku Delphi klasę deklaruje się z użyciem słowa kluczowego class:
type
NazwaKlasy = class (KlasaBazowa)
Metody
end;
Klasę należy deklarować jako nowy typ danych. Czyli:
- klasa musi zostać zadeklarowana z użyciem słowa kluczowego class,
- klasa musi być deklarowana jako typ (type),
- klasa musi mieć nazwę,
- klasę należy zakończyć słowem kluczowym ? End;.
Zwróćmy uwagę, że po słowie kluczowym class nie ma średnika!
Najprostsza deklaracja klasy może wyglądać następująco:
type
MojaKlasa = class
end;
Od tego momentu będzie można zadeklarować nową zmienną typu MojaKlasa:
var
Moja : MojaKlasa;
Skoro klasa jest deklarowana jako typ, w każdym momencie można utworzyć zmienną wskazującą na ów typ. Taka konstrukcja jak powyżej jest nazywana tworzeniem instancji klasy lub egzemplarza klasy. Możesz się spotkać również z pojęciem tworzenia obiektu klasy.
Zalecane jest specjalne nazewnictwo w przypadku klas, z uwzględnieniem litery T jako pierwszej litery nazwy klasy ? np. TObject, TRegistry, TMojaKlasa itp. Taka konwencja przyjęła się dzięki bibliotece VCL, w której nazwa każdej klasy jest poprzedzana literą T. Nie zamierzam odbiegać od tej reguły ? swoje klasy także będę nazywał, zaczynając od litery T.
Do czego służą klasy?
Początkującemu programiście zapewne trudno będzie dostrzec zalety korzystania z klas. Zawsze można zrealizować ten sam cel, posługując się zmiennymi globalnymi oraz procedurami i funkcjami. To fakt, jednak stosowanie klas daje spore możliwości, z których warto wymienić: umieszczanie w niej odpowiednich metod i ich ukrywanie, albo też tworzenie kilku instancji danej klasy. Ponadto umożliwia uporządkowanie kodu i podzielenie go na kilka oddzielnych części, z których każda wykonuje inną czynność podczas działania programu.
Przykładowo, aby złożyć komputer, nie muszę wiedzieć, jak dokładnie działa procesor i z jakich elementów jest zbudowany. Wystarczy że wiem, że jest to centralna jednostka komputera i że bez procesora nie uruchomię całości. Muszę także wiedzieć, gdzie włożyć ten procesor i jak go przymocować.
Kierowca samochodu nie musi wiedzieć, co auto ma pod maską, jakie są parametry jego silnika, jak działa skrzynia biegów i co powoduje, że całość się porusza. Wystarczy że wie, iż do uruchomienia samochodu potrzebne są kluczyki ? musi również umieć posługiwać się kierownicą, dźwignią zmiany biegów i pedałami.
Jeżeli wraz ze swoimi wspólnikami projektujecie jakąś większą aplikację, każdy może zająć się przydzielonym zadaniem ? przykładowo, ktoś zajmuje się utworzeniem klasy służącej do wyszukiwania plików na dysku, jeszcze ktoś tworzeniem innej klasy, a inna osoba jedynie wszystko koordynuje i łączy w całość. Nie musi ona wiedzieć, w jaki sposób działa klasa wyszukująca pliki, ale musi wiedzieć, jak ją połączyć z resztą programu, tak aby wszystko działało zgodnie z oczekiwaniami. Tego z kolei można się dowiedzieć z instrukcji (czyli z dokumentacji dostarczonej przez autora klasy).
Hermetyzacja
Pojęcie hermetyzacji jest związane z ukrywaniem pewnych danych. Klasy udostępniają na zewnątrz pewien interfejs opisujący działanie takiej klasy i tylko z tego interfejsu może korzystać użytkownik. Bowiem klasy mogą zawierać dziesiątki, a nawet setki metod (procedur lub funkcji), które wykonują różne czynności. My jako projektanci klasy powinniśmy zapewnić dostęp jedynie do niektórych metod, tak aby potencjalny użytkownik nie mógł wykorzystywać wszystkich, gdyż może to spowodować nieprzewidywalne działanie programu, zawieszanie itp.
Wewnątrz silnika samochodu też dochodzi do pewnych procesów, ale kierowca nie musi o nich wiedzieć. Informacji tych nie potrzebuje także inny element silnika, który się z nim łączy ? komunikowanie się pomiędzy elementami przebiega ustalonym strumieniem i to wystarczy.
Delphi pozwala na ukrywanie kodu w klasie, w tym celu stosuje się pewne klauzule, o których powiemy sobie później.
Metody są procedurami i funkcjami znajdującymi się w klasie i współpracującymi z sobą w celu wykonania konkretnych czynności.
Dziedziczenie
Cała wizualna biblioteka VCL jest oparta na dziedziczeniu, które można określić jako podstawowy fundament budowania klas.
Powróćmy do przykładu z silnikiem. Projektanci, chcąc ulepszyć dany silnik, mogą nie zechcieć zaczynać od zera. Byłaby to zwyczajna strata czasu. Nie lepiej po prostu unowocześnić silnik już istniejący?
Przykład z silnikiem można zastosować do klas. Aby zbudować nową, bardziej funkcjonalną klasę, można przejąć możliwości starej. Taki proces nazywamy w programowaniu dziedziczeniem. Na przykład w VCL podstawową klasą jest klasa o nazwie TObject
. Zaprogramowano w niej podstawowe mechanizmy oraz funkcje. Klasą, która dziedziczy po niej, jest TRegistry
. Inaczej mówiąc, przejmuje ona wszystkie dotychczasowe możliwości TObject
oraz dodaje własne ? w tym przypadku obsługę rejestru Windows.
W takim przypadku klasę TObject
nazywamy klasą potomną (lub po prostu potomkiem), a TRegistry
klasą dziedziczną.
Polimorfizm
Pojęcie polimorfizmu jest związane z dziedziczeniem. Jest to po prostu możliwość tworzenia wielu metod o tej samej nazwie. Przykładowo, jeżeli klasa A
zawiera metodę XYZ
i jeżeli klasa B
dziedziczy po klasie A
, to tym samym posiada również jej metodę XYZ
. Teraz istnieje możliwość ?przykrycia? owej metody XYZ
i zaimplementowanie takiej samej, tyle że w klasie B
.
Instancja klasy
Aby móc wykorzystywać metody znajdujące się w klasie, należy utworzyć tzw. instancję owej klasy. W tym momencie zostaje zarezerwowana pamięć potrzebna do wykonania metod znajdujących się w tej klasie. Istotną sprawą jest to, że może istnieć wiele instancji danej klasy. Jest to przewaga w stosunku do idei programowania strukturalnego. Każda instancja rezerwuje osobny blok pamięci. Ewentualne zmienne (pola) znajdujące się w obrębie klasy korzystają z osobnych przestrzeni adresowych i mogą mieć różne wartości.
Konstruktor
Aby utworzyć klasę i móc ją stosować, należy skorzystać z tzw. konstruktora. Konstruktor jest specjalną metodą dziedziczoną po klasie TObject
(w przypadku VCL), za pomocą której jest możliwe zainicjalizowanie klasy.
Oto przykład inicjalizacji klasy:
var
Klasa : TMojaKlasa; // wskazanie na nowy typ ? klasę
begin
Klasa := TMojaKlasa.Create; // wywołanie konstruktora
end;
Jak widać, konstrukcja jest w tym przypadku dość specyficzna:
Zmienna := NazwaKlasy.Create;
Taka instrukcja spowoduje utworzenie klasy ? od tej pory można z niej dowolnie korzystać.
Destruktor
Destruktor jest specjalną metodą utworzoną w celu destrukcji klasy, czyli zwolnienia pamięci. Tak więc konstruktor powinien być wywoływany na samym początku ? przed skorzystaniem z klasy, a destruktor ? na samym końcu, kiedy dana klasa już jest niepotrzebna. Destruktor również jest metodą dziedziczoną po klasie TObject ? oto przykład jej wywołania:
var
Klasa : TMojaKlasa; // wskazanie na nowy typ ? klasę
begin
Klasa := TMojaKlasa.Create; // wywołanie konstruktora
Klasa.Free; // zwolnienie klasy
end;
Destrukcja klasy odbywa się poprzez wywołanie metody Free
.
Destruktory w .NET
Ten fragment został opisany w osobnym artykule: Destruktory w .NET
Pola
Pojęcie zostało opisane w osobnym artykule.
Metody
Pojęcie zostało opisane w osobnym artykule.
Właściwości
Pojęcie zostało opisane w osobnym artykule.
Zobacz też:
W klasach nie ma zmiennych tylko są pola :-P
Jasne, ale pojecie "zmienna" jest uzywane zamiennie do "pola", aby wprowadzic nowe pojecie dla poczatkujacych, i tymsamym aby nie wprowadzac zametu.
...no to napisz!
Jeszcze można napisać trochę więcej o klasach :P