Podstawy programowania obiektowego

Transkrypt

Podstawy programowania obiektowego
Podstawy Programowania – semestr drugi
Wykład dwunasty
1.
Postawy programowania obiektowego
Dotychczas tworzone przez nas programy były programami strukturalnymi. Oznacza, to że kod tych programów był podzielony na podprogramy działające na określonych przez nas zmiennych. Przepływ sterowania pomiędzy tymi podprogramami, czyli kolejność w jakiej są one wywoływane jest określona jawnie. Podejście strukturalne, mimo wielu zalet, takich jak prostota zapisu programu, możliwość wielokrotnego wykorzystania kodu, ma też liczne wady. Do najpoważniejszych należą oddzielenie kodu od danych na których przeprowadza on operacje oraz ubogi mechanizm kontroli widoczności zmiennych. Pierwsza z nich powoduje, że pisząc program myślimy o rozwiązywanym problemie nie w kategoriach tego problemu, ale w kategoriach języka programowania, który służy nam do zapisania kodu programu (np.: „wywołaj funkcję wyszukującą w tablicy rekord zawierający dane studenta o określonym nazwisku”). Ponadto, jeśli swój kod udostępnimy innym programistom, np.: w postaci biblioteki, to może się okazać, że zignorują oni napisane przez nas funkcje i procedury i będą bezpośrednio odwoływać się do zmiennych i struktur danych lub będą niewłaściwie posługiwać się stworzonymi przez nas podprogramami. W obu przypadkach takie działanie może skończyć się katastrofą. Druga wada wiąże się z opisanym przed chwilą zagrożeniem, jak również wprowadza nowe – w kodzie dużych programów, tworzonych na potrzeby dużych projektów informatycznych bardzo trudno jest zapanować nad nazwami zmiennych, co bardzo często prowadziło do załamania się prac nad takimi projektami i przynosiło firmom ogromne straty 1. Odpowiedzią specjalistów od inżynierii oprogramowania na te problemy było wprowadzenie nowej metodologii tworzenia programów, którą nazwano techniką obiektową. To pojęcie obejmuje trzy osobne zagadnienia: analizę obiektową (ang. OOA = Object Oriented Analysis), projektowanie obiektowe (ang. Object Oriented Design) i programowanie obiektowe (ang. Object Oriented Programming). Ten wykład będzie obejmował ostanie zagadnienie, do dwóch pierwszych wrócimy innym razem. Z pojęciem obiektowości wiąże się pojęcie abstrakcyjnych typów danych, z którym spotkaliśmy się przy okazji omawiania dynamicznych struktur danych. Takim typem był np.: stos. Aby go zdefiniować musieliśmy określić typ pojedynczego elementu takiego stosu (typ bazowy stosu) i operacje, które możemy na tych elementach przeprowadzać. Zgodnie z tym co zostało napisane wyżej, programista korzystający z naszego kodu może te operacje ominąć implementując ich własne wersje. Programowanie obiektowe idzie o krok dalej: pozwala zadeklarować zmienne i związać z nimi operacje, tak, aby nie można ich było rozdzielić. W dużym uproszczeniu można określić programowanie obiektowe jako programowanie polegające na definiowaniu nowych typów danych, opisujących pojęcia właściwe dla rozwiązywanego problemu. Takie typy danych będziemy nazywać klasami, a zmienne tych typów – obiektami. Klasa Człowiek opisuje 2 (niektóre) cechy wspólne dla wszystkich ludzi. Obiektem tej klasy, jest konkretny człowiek, posiadający ściśle określone imię, nazwisko i wiek. Spróbujmy przedstawić zamieszczony przykład w postaci kodu w języku Pascal:
Przykład:
Klasa: Człowiek – każdy człowiek posiada imię, nazwisko, wiek.
Obiekt: konkretna instancja klasy, np.: Jan Kowalski, lat 37
Klasę obiektu definiujemy w języku Object Pascal za pomocą słowa kluczowego „object”3. Definicja klasy podobna jest w zapisie do definicji rekordu, ale oprócz deklaracji pól występują w niej deklaracje podprogramów, które będą na nich operowały. Te podprogramy będziemy nazywać metodami. Pola klasy z kolei nazywa się atrybutami. Należy zwrócić uwagę, że w miejscu definicji nazwy metod są poprzedzane nazwą klasy, do której należą. Wprawdzie nie jest to koniecznością, ale można przyjąć jakiś dogodny schemat nazywania klas, metod i pól. W języku Object Pascal, który stanowi rozszerzenie Pascala, stworzone przez firmę Borland najczęściej nazwa klasy jest poprzedzana literą „T”. Istnieje jednak, inna, powszechniej wykorzystywana notacja, w której nazwy klas pisze się z wielkiej litery. Jeśli składają się one z wielu wyrazów, to pierwsze znaki tych wyrazów są również pisane wielkimi literami. W przypadku metod i pól, ich nazwy są pisane małymi literami, chyba, że składają się z wielu wyrazów, wówczas pierwszy jest pisany małą literą a w pozostałych tylko pierwsze litery są wielkie. W wierszu 51 została stworzona zmienna typu „Czlowiek”, która jest po prostu obiektem klasy „Czlowiek”. Dostęp do składowych (czyli metod i pól) tego obiektu jest realizowany według schematu:
1 program obiekty;
2 3 uses crt;
4 5 type
6 7 ciag = string[40];
8 9 Czlowiek = object
10 imie,nazwisko:ciag;
11 wiek:byte;
12 function podajWiek:byte;
13 procedure ustawWiek(w:byte);
14 function podajNazwisko:ciag;
nazwaObiektu.nazwaPola;
15 procedure ustawNazwisko(const n:ciag);
lub
nazwaObiektu.nazwaMetody;
16 function podajImie:ciag;
17 procedure ustawImie(const i:ciag);
18 end;
19 Wywołanie metody związanej z obiektem i realizującej określone działania na danych należących do tego obiektu jest nazywane wysłaniem komunikatu do obiektu. 20 function Czlowiek.podajWiek:byte;
21 begin
1
2
3
Nie oznacza to, że nie da się stworzyć dużego projektu używając metodologii strukturalnej. Przykładem przedsięwzięcia programistycznego, które prawie w całości korzysta z niej (poza niewielkimi fragmentami kodu) jest jądro systemu Linux.
W przykładzie nie uwzględniono przykładowych czynności jakie może wykonywać człowiek.
Większość języków programowania używa w tym celu słowa kluczowego "class". We Free Pascalu, który podejście do obiektowości wzoruje głównie na kompilatorach Borland Pascal, też jest ono wykorzystywane, ale do określania klas obiektów, które tworzone są wyłącznie dynamicznie. Na razie tym przypadkiem nie będziemy się zajmować, bo wymaga on wprowadzenia dodatkowych pojęć, które wymagają większej wiedzy. Niestety, niektóre rozwiązania firmy Borland w zakresie obiektowości w języku Pascal, zwłaszcza w środowisku Turbo Pascal, są dosyć kontrowersyjne.
1
Podstawy Programowania – semestr drugi
Okazuje się, że w zamieszczonym obok programie, mimo, że związaliśmy dane z operacjami 4, które są na nich wykonywane, to nadal możemy do tych danych sięgnąć bezpośrednio. Aby zabronić takiego dostępu można posłużyć się modyfikatorami dostępu, czyli słowami kluczowymi określającymi rodzaj dostępu do poszczególnych składowych obiektu5. W wersji języka Object Pascal, która jest dostępna w środowisku Turbo Pascal istnieją tylko dwa słowa tego typu: public i private. Pierwsze oznacza, że dostęp do składowych poprzedzonych tym słowem mają wszystkie inne obiekty w programie, oraz elementy nieobiektowe, czyli są publiczne. Drugie oznacza, że tylko obiekt ma do nich dostęp, czyli są prywatne. W środowisku Free Pascal dostępny jest także modyfikator protected. Używany on jest w technice wielokrotnego wykorzystania kodu, która nazywa się dziedziczeniem i będzie objaśniona na następnym wykładzie. Możemy użyć słów public i private w tym samym pliku, gdzie znajduje się program główny, ale słowo private w takim przypadku nie zadziała prawidłowo. Domyślnie wszystkie składowe klasy zdefiniowanej w programie głównym są publiczne. Aby uczynić niektóre z nich prywatnymi należy klasę zdefiniować w module i tam użyć słów private i public. Takie rozwiązanie zaprezentowano na dwóch kolejnych listingach. 22 podajWiek:=wiek;
23 end;
24 25 procedure Czlowiek.ustawWiek(w:byte);
26 begin
27 if (w>0) and (w<130) then wiek:=w;
28 end;
29 30 function Czlowiek.podajNazwisko:ciag;
31 begin
32 podajNazwisko:=nazwisko;
33 end;
34 35 procedure Czlowiek.ustawNazwisko(const n:ciag);
36 begin
37 nazwisko:=n;
38 end;
39 40 function Czlowiek.podajImie:ciag;
41 begin
42 podajImie:=imie;
43 end;
44 45 procedure Czlowiek.ustawImie(const i:ciag);
46 begin
47 Imie:=i;
48 end;
49 50 var
51 c:Czlowiek;
52 53 begin
54 clrscr;
55 c.ustawImie('Jan');
56 c.ustawNazwisko('Kowalski');
57 c.ustawWiek(24);
58 writeln(c.podajImie);
59 writeln(c.podajNazwisko);
60 writeln(c.podajWiek);
61 readln;
62 end.
4
5
W terminologii obiektowej nazywa się to enkapsulacją.
Tą czynność w terminologii obiektowej nazywa się z kolei hermetyzacją. Często swoim znaczeniem obejmuje również enkapsulację.
2
Podstawy Programowania – semestr drugi
Pierwszy listing przedstawia kod modułu o nazwie „human”, w którym została umieszczona definicja klasy. W tej definicji użyto modyfikatorów dostępu public i private, aby określić rodzaj dostępu do poszczególnych składowych, zgodnie z regułami, które zostały wyżej opisane. Należy zwrócić uwagę, że zasięg modyfikatora rozpościera się od miejsca jego wystąpienia, do końca definicji klasy lub pojawienia się nowego modyfikatora. Przykład prezentuje typowe użycie modyfikatorów. Zazwyczaj są one wykorzystywane do uczynienia danych (pól) obiektu prywatnymi, niedostępnymi na zewnątrz, a metod publicznymi, dostępnymi również na zewnątrz obiektu. Dosyć rzadko udostępnia się dane również do użytku publicznego (najczęściej ma to związek z optymalizacją czasu działania programu). Prywatnymi możemy uczynić nie tylko pola, ale i metody, które stanowią część mechanizmu działania obiektu i nie powinny być widoczne na zewnątrz. Takimi metodami mogą być np.: metody sortujące tablicę, która jest polem w obiekcie. To działanie nazywa się ukrywaniem implementacji. Definicja klasy jest umieszczona w części interfejsowej modułu, natomiast definicje metod obiektu zostały umieszczone w części implementacyjnej. W kodzie programu głównego ten moduł został włączony i wykorzystany do stworzenia obiektu klasy „Czlowiek” oraz zaprezentowania jego działania.
1 unit human;
2 3 interface
4 5 type
6 7 ciag = string[40];
8 9 Czlowiek = object
10 11 private
12 imie,nazwisko:ciag;
13 wiek:byte;
14 15 public
16 function podajWiek:byte;
17 procedure ustawWiek(w:byte);
18 function podajNazwisko:ciag;
19 procedure ustawNazwisko(const n:ciag);
20 function podajImie:ciag;
21 procedure ustawImie(const i:ciag);
22 23 end;
24 25 implementation
26 27 function Czlowiek.podajWiek:byte;
28 begin
29 podajWiek:=wiek;
30 end;
31 32 procedure Czlowiek.ustawWiek(w:byte);
33 begin
34 if (w>0) and (w<130) then wiek:=w;
35 end;
36 37 function Czlowiek.podajNazwisko:ciag;
38 begin
39 podajNazwisko:=Nazwisko;
40 end;
41 42 procedure Czlowiek.ustawNazwisko(const n:ciag);
43 begin
44 nazwisko:=n;
45 end; 46
3
Podstawy Programowania – semestr drugi
47 function Czlowiek.podajImie:ciag;
48 begin
49 podajImie:=imie;
50 end;
51 52 procedure Czlowiek.ustawImie(const i:ciag);
53 begin
54 imie:=i;
55 end;
56 57 end.
1 program obiekty;
2 3 uses crt, human;
4 5 var
6 c:Czlowiek;
7 8 begin
9 clrscr;
10 c.ustawImie('Jan');
11 c.ustawNazwisko('Kowalski');
12 c.ustawWiek(24);
13 writeln(c.podajImie);
14 writeln(c.podajNazwisko);
15 writeln(c.podajWiek);
16 readln;
17 end.
2.
Paradygmat programowania obiektowego, czyli trochę teorii
Skoro wiemy już co to jest klasa, metoda, obiekt i jak te elementy są tworzone w programie napisanym w języku Object Pascal, to poznajmy bardziej formalną definicję tego, czym jest program obiektowy. Pierwszym językiem programowania obiektowego, który odniósł sukces komercyjny, był język Smalltalk. Alan Kay scharakteryzował ten język w pięciu punktach 6, które opisują również programowanie z wykorzystaniem obiektów:
1.
Wszystko jest obiektem. Każdy element programu obiektowego (zakładając, że mamy do czynienia z „czystym” programowaniem obiektowym) jest specjalną zmienną, która posiada pewien stan (dane) i pewne zachowanie (metody). Teoretycznie każdy element świata rzeczywistego (tzw. przestrzeni problemu) może być obiektem (książka, samochód, człowiek, kangur, itd.)
2.
Program jest zbiorem obiektów, które poprzez wysyłanie komunikatów określają sobie wzajemnie, jaką czynność należy wykonać . Termin „wysłanie komunikatu” oznacza wywołanie metody, czyli zażądanie wykonania pewnej czynności. 3.
Każdy obiekt posiada własny typ. Typ obiektu określa, jakie komunikaty mogą być do niego wysłane, tak więc o typie obiektu decyduje jego klasa. W programowaniu obiektowym dosyć często zamiast mówić „typ obiektu” mówimy „klasa obiektu”. Obiekt jest więc zmienną pewnej klasy lub inaczej – instancją pewnej klasy. Najważniejszą cechą obiektów, którą określa klasa jest, jakie komunikaty można wysłać do jej obiektów.
4.
Każdy obiekt posiada własną pamięć, na którą składają się inne obiekty . To stwierdzenie będzie bardziej zrozumiałe, kiedy poznamy technikę kompozycji i dowiemy się czym są obiekty zagnieżdżone. Nowe obiekty możemy tworzyć łącząc w grupę istniejące obiekty. Pozwala to nam pisać złożone programy, których struktura jest ukryta pod prostotą obiektu.
5.
Wszystkie obiekty danego typu mogą otrzymywać te same komunikaty. Ten zapis nabierze szczególnego znaczenia, kiedy będziemy omawiać technikę dziedziczenia. Załóżmy, że piszemy program rysujący obiekty graficzne. Obiekt typu Okrąg jest również obiektem klasy Figura. Oznacza to, że może otrzymywać te same komunikaty co obiekt Figura. Pozwala to napisać program odwołujący się do obiektów typu Figura, który automatycznie będzie obsługiwał obiekty typu Okrąg.
3.
Podsumowanie
W prezentowanych przykładach pokazano niewielką część możliwości programowania obiektowego. Idea ta pozwala bardziej zbliżyć się do przestrzeni rozwiązywanego problemu, gdyż operujemy pojęciami właściwymi temu problemowi. Możemy np.: stworzyć obiekt przechowujący inne obiekty, które przechowują informację o studentach i wysłać do niego komunikat: „znajdź studenta o podanym nazwisku”. Po nabraniu pewnej wprawy kod programu obiektowego staje się bardziej czytelny i łatwy do zrozumienia niż kod programu strukturalnego. Na następnych wykładach dowiemy się również, że programowanie obiektowe pozwala na znacznie lepsze ponowne wykorzystanie kodu, niż programowanie strukturalne.
6
Ten fragment wykładu jest napisany na podstawie książki „Thinking in Java” Bruce'a Eckela wydanej przez Wydawnictwo Helion, Gliwice, 2001
4

Podobne dokumenty