1 Obiektowe Języki Programowania Patryk Jasik Katedra Fizyki Teoretycznej i Informatyki Kwantowej Wydział Fizyki Technicznej i Matematyki Stosowanej Politechnika Gdańska Pokój 415GB E-mail: [email protected]@mif.pg.gda.pl Strona domowa: http://aqualung.mif.pg.gda.plhttp://aqualung.mif.pg.gda.pl Konsultacje: czwartek 9 - 11
2 Warunki zaliczenia przedmiotu Pisemny sprawdzian wiedzy z wykładu (10 pkt) Krótkie sprawdziany („wejściówki”) na początku 5 zajęć laboratoryjnych (10 pkt) Sprawdzian z programowania w języku C++ ISO/ANSI (10 pkt) Projekt programistyczny i jego ewentualna obrona język C++ CLI i C# (20 pkt) Uzyskanie 50% punktów spośród wszystkich możliwych do zdobycia (25 pkt/50 pkt) Termin zaliczenia wykładu: 11.06 Terminy sprawdzianów z programowania C++ ISO/ANSI – 14.04 i 15.04 Oddanie projektu C++ CLI – 18.05, dzień spóźnienia -2 pkt Oddanie projektu C# – 15.06, dzień spóźnienia -2 pkt Termin poprawkowy (wykład + programowanie): 25.06
3 Warunki zaliczenia przedmiotu PunktyOcena 50bdb plus 49,5 - 45bdb 44,5 - 40db plus 39,5 - 35db 34,5 - 30dst plus 29,5 - 25dst
4 Literatura I. Sommerville – „Inżynieria oprogramowania”, WNT, Warszawa 2003 B. Meyer – „Programowanie zorientowane obiektowo”, Helion 2005 B. Stroustrup – „Język C++”, WNT, Warszawa 2004 J. Grębosz – „Symfonia C++ Standard”, Oficyna Kallimach, Kraków 2005 J. Grębosz – „Pasja C++”, Oficyna Kallimach, Kraków 2003 S.B. Lippman, J. Lajoie – „Podstawy języka C++”, WNT, Warszawa 2003 L.C. Tondo, P.B. Leung – „Podstawy języka C++ - ćwiczenia i rozwiązania”, WNT, Warszawa 2001 B. Eckel – „Thinking in C++”, Helion, 2002 N. M. Josuttis – „C++. Projektowanie zorientowane obiektowo”, Helion Piotr Wróblewski – „Język C++ dla programistów”, Helion D.R. Stephens, C. Diggins, J. Turkanis, J. Cogswell, „C++. Receptury”, Helion, 2006 I. Horton, „Visual C++ 2005 od podstaw”, Helion 2007
5 Literatura B. Eckel – „Thinking in Java”, Helion 2006 L. Lemay, R. Cadenhead – „Java 2 – dla każdego”, Helion 2001 J. Knudsen, P. Niemeyer – „Java. Wprowadzenie”, Helion 2003 M. Lis – „Java – ćwiczenia praktyczne”, Helion 2002 B. Boone – „Java dla programistów C i C++”, WNT 1998 I.F. Darwin – „Java. Receptury”, Helion J. Hilyard, S. Teilhet, „C#. Receptury”, Helion 2006 M. Lis, „Ćwiczenia C#”, Helion 2006 S. Orłowski, „C#. Tworzenie aplikacji sieciowych”, Helion 2007 J. Liberty, „C#. Programowanie”, Helion 2005 J. Liberty, B. MacDonald, „C#. Wprowadzenie”, Helion 2005 K. Hoffman, „Microsoft Visual C# 2005. Księga eksperta ”, Helion 2007
6 Historia BCPL, Richards, 1967 – prototyp języka C Język B, Thompson, 1969 – prototyp języka C Simula 67, Dahl, 1970 – pojęcie klasy wraz z klasami pochodnymi i funkcjami wirtualnymi Algol 68, Woodward, 1974 – przeciążanie operatorów i możliwość umieszczania deklaracji wszędzie tam, gdzie są dozwolone instrukcje Język C, Kernighan, Ritchie, 1978 – prototyp języka C++ Język ANSI C, Kernighan, Ritchie, 1988 ADA, Ichbiah, 1979 – obsługa wyjątków CLU, Liskov, 1979 – obsługa wyjątków C z klasami, Stroustrup, 1980 – rozszerzenie języka C C++, Stroustrup, lipiec 1983 – pierwsze zastosowanie poza pracownią badawczą Standard ISO C++, Stroustrup, 1998, poprawki do standardu 2003, nowy standard 2009 Java, Gosling, 1994 C#, Hejlsberg, początek XXI wieku
7 Pierwszy program #include int main() { printf ("Witajcie! To jest wyklad z obiektowych jezykow programowania."); }
8 Pierwszy program #include using namespace std; int main() { cout
9 Drugi program #include int main() { int x, y, liczba; printf(„Podaj pierwsza liczbe: \n"); scanf(„%i”, &x); printf(„Podaj druga liczbe: \n"); scanf(„%i”, &y); liczba = x+y; printf("To jest nasz wynik: x+y=%i\n", liczba); }
10 Drugi program #include using namespace std ; int main() { int stopy ; // to do przechowywania liczby stop float metry ; // do wpisania wyniku float przelicznik = 0.3 ; // przelicznik: stopy na metry cout > stopy ; // przyjecie danej z klawiatury metry = stopy * przelicznik; // wlasciwe przeliczenie cout
11 Połączenie czterech kluczowych elementów leży u podstaw techniki programowania obiektowego Metoda budowania systemów Wymagania dotyczące niezawodności Zasada epistemologiczna Technika klasyfikacji
12 Metoda budowania systemów Wielokrotne wykorzystanie części składowych, z których tworzone jest oprogramowanie Każdy program komputerowy wykonuje pewne czynności na obiektach różnych typów Aby program był elastyczny i łatwo przystosowywał się do różnych zastosowań, należy skupić jego strukturę wokół obiektów, a nie akcji, jakie ma wykonywać Opracowanie bardzo wszechstronnego i dającego duże możliwości mechanizmu klas W tworzeniu systemów zorientowanych obiektowo klasy pełnią rolę fundamentu zarówno modułowej struktury programu, jak i systemu typów.
13 Wymagania dotyczące niezawodności Stanowią wyraz radykalnego podejścia do tworzenia oprogramowania, które ma robić dokładnie to, czego się od niego wymaga Każdy system traktuje się jako zbiór komponentów, które współpracują ze sobą w taki sam sposób jak sprawnie działające przedsiębiorstwa Każdy z komponentów musi wypełniać zobowiązania jasno określone w kontrakcie i ma prawo czerpać z tego korzyści, które również zostały w nim omówione
14 Zasada epistemologiczna Epistemologia - teoria poznania, dział filozofii, zajmujący się relacjami między poznawaniem, poznaniem a rzeczywistością. Epistemologia rozważa naturę takich pojęć jak: prawda, przekonanie, sąd, spostrzeganie, wiedza czy uzasadnienie.
15 Zasada epistemologiczna W technice obiektowej obiekty opisywane są przez klasy Definicje klas mówią jedynie, co możemy robić z obiektami Klasy składają się z listy dozwolonych operacji (zwanych też możliwościami klasy) i z list formalnych właściwości tych operacji (tak zwanych kontraktów) Wszystko opisywane jest dokładnie przez teorię abstrakcyjnych typów danych W świecie modelowania systemów informatycznych zakłada się obecność pewnej „zewnętrznej rzeczywistości”, która istnieje znacznie dłużej, niż jakiekolwiek oprogramowanie odwołujące się do niej. Jest to pozbawione sensu dla programisty stosującego techniki obiektowe, ponieważ rzeczywistość nie istnieje w oderwaniu od tego, co chcemy z nią zrobić. Programista nie zastanawia się nad tym, czy rzeczywistość w ogóle istnieje, czy nie, ponieważ jedyne co wie, to z czego może skorzystać. Wszystkie posiadane przez niego informacje na temat danej rzeczy sprowadzają się do tego, w jaki sposób można ją wykorzystać.
16 Technika klasyfikacji Wywodzi się z obserwacji, iż w każdej usystematyzowanej pracy umysłowej, a w szczególności przy prowadzenie rozważań o charakterze naukowym, konieczne jest opracowanie taksonomii dla badanych dziedzin. Technika obiektowa opiera się w dużym stopniu na metodzie klasyfikacji zwanej dziedziczeniem
17 FAQ W jaki sposób dowiadujemy się o nowych klasach i je opisujemy? Jak tworzone przez nas programy powinny manipulować klasami i odpowiadającymi im obiektami (egzemplarzami tych klas)? Jakie relacje mogą zachodzić między klasami? W jaki sposób możemy wykorzystać fakt, że między wieloma klasami często występują podobieństwa? Odpowiedzi: polimorfizm i wiązanie dynamiczne, nowe podejście do typów i kontroli typów, generyczność (zarówno ograniczona, jak i nieograniczona), ukrywanie informacji, asercje, bezpieczna obsługa wyjątków i automatyczne zwalnianie pamięci.
18 Główne cele programowania zorientowanego obiektowo Poprawność – to zdolność programów komputerowych do wykonywania dokładnie tych zadań, które im powierzono i które zdefiniowano w ich specyfikacjach (sprzęt – system operacyjny – kompilator – biblioteki – aplikacja). Odporność – to zdolność systemów komputerowych do prawidłowego reagowania na niestandardowe sytuacje. Brak katastrofy – wyświetlenie stosownych komunikatów o błędzie lub wejście w „tryb łagodnego zejścia”. Rozszerzalność – to możliwość łatwego dostosowywania programu komputerowego do zmian w specyfikacji (prostota i decentralizacja). Przystosowanie do wielokrotnego użycia – to zdolność elementów składowych oprogramowania do pełnienia roli elementów konstrukcyjnych wielu różnych aplikacji.
19 Główne cele programowania zorientowanego obiektowo Zgodność – to łatwość, z jaką można ze sobą łączyć poszczególne składniki oprogramowania. Kluczem do zgodności jest homogeniczność projektu i wypracowanie jednego standardu przesyłania informacji wewnątrz aplikacji: –standaryzowane formaty plików, np. tak jak w systemie operacyjnym Unix, w którym każdy plik tekstowy jest po prostu sekwencją znaków, –standaryzowane struktury danych, np. tak jak w programach napisanych w języku Lisp, w których wszystkie dane i programy są reprezentowane przez drzewa binarne, nazywane listami, –standaryzowane interfejsy użytkownika, np. tak jak w różnych wersjach systemu Windows, OS/2 czy MacOS, w których wszystkie narzędzia komunikują się z użytkownikiem w taki sam sposób, oparty na wykorzystaniu standardowych komponentów, takich jak okna, ikony, menu itd. Wydajność – to zdolność programu do stawiania komputerowi jak najmniejszych wymagań sprzętowych, rozumianych między innymi jako czas procesora, przestrzeń zajmowana w pamięci wewnętrznej i zewnętrznej, szerokość pasma wykorzystywanego przy przesyłaniu danych.
20 Główne cele programowania zorientowanego obiektowo Przenośność – to łatwość, z jaką dane oprogramowanie daje się przenosić między różnymi środowiskami sprzętowymi i programowymi Łatwość obsługi – to łatwość, z jaką ludzie o różnym doświadczeniu i kwalifikacjach mogą nauczyć się korzystania z aplikacji i stosowania ich do rozwiązywania problemów. Termin ten obejmuje też łatwość instalacji i nadzorowania pracy systemu. Zasada projektowania interfejsu użytkownika: Nie udawaj, że znasz użytkownika. Nie znasz! Funkcjonalność – to zbiór możliwości oferowanych przez system. Adekwatność czasowa – to gotowość programu komputerowego do wypuszczenia na rynek wtedy, gdy użytkownicy go potrzebują lub jeszcze zanim pojawi się taka potrzeba.
21 Główne cele programowania zorientowanego obiektowo Weryfikowalność – to łatwość przygotowywania procedur pozwalających określić, czy produkt znajduje się na akceptowalnym poziomie. W szczególności chodzi tu o dane testowe i procedury pozwalające na wykrywanie niepowodzeń i odnajdywanie tych błędów popełnionych na etapie walidacji danych i w fazie operacyjnej, które są przyczyną niepowodzeń Integralność – to zdolność oprogramowania do chronienia swoich składników (programów, danych) przed nieautoryzowanym dostępem i modyfikacjami Naprawialność – to udostępnianie możliwości naprawiania defektów Ekonomiczność – to uzupełnienie adekwatności czasowej, czyli zdolność systemu do zmieszczenia się w założonym budżecie
22 Kartezjusz – Rozprawa o metodzie (1637) Drugim [prawem, jakie postanowiłem przestrzegać, było] – podzielić każde z rozpatrywanych zagadnień na tyle cząstek, na ile się da i ile będzie wymagać lepsze rozwiązanie. Trzecim – prowadzić myśli po porządku, zaczynając od przedmiotów najprostszych i najłatwiejszych do poznania, aby następnie wznosić się pomału jak gdyby po stopniach, aż do poznania bardziej złożonych; należy się przy tym domniemywać prawidłowych związków nawet między tymi, które nie tworzą naturalnego szeregu.
23 Pięć kryteriów modułowości Dekompozycyjność – pomaga w rozkładaniu danego problemu programistycznego na pewną (niedużą) liczbę mniej złożonych problemów cząstkowych, tworzących wspólnie prostą strukturę, ale na tyle niezależnych, aby pracę nad każdym z nich można było prowadzić niezależnie od pozostałych. Liczbę zależności między systemami cząstkowymi należy sprowadzić do minimum. Kompozycyjność – zachęca do tworzenia składników oprogramowania, które można potem swobodnie ze sobą łączyć i w ten sposób tworzyć nowe programy, czasem nawet w innym środowisku niż to, w którym zostały one opracowane.
24 Pięć kryteriów modułowości Zrozumiałość – pomaga w pisaniu programów, których każdy moduł jest zrozumiały dla czytelnika nieznającego pozostałych modułów. Ciągłość – sprzyja tworzeniu aplikacji o takiej architekturze, że drobna zmiana specyfikacji problemu powoduje konieczność zmodyfikowania tylko jednego lub kilku modułów. Ochrona – prowadzi do tworzenia architektur, w których skutki wystąpienia w czasie wykonywania programu nienormalnej sytuacji w którymś z modułów są odczuwalne jedynie w tym module, a w najgorszym wypadku dają się we znaki kilku sąsiednim modułom.
25 Pięć reguł modułowości Mapowanie bezpośrednie – struktura modułowa opracowana w procesie tworzenia programu komputerowego powinna pozostawać zgodna z każdą strukturą modułową opracowaną w procesie modelowania problemu z danej dziedziny. Niewiele interfejsów – każdy moduł powinien komunikować się z tak małą liczbą innych modułów, jak to tylko możliwe. Nieduże interfejsy (słabe sprzężenia) – jeżeli dwa moduły komunikują się ze sobą, powinny wymieniać tylko tyle informacji, ile jest absolutnie niezbędne.
26 Pięć reguł modułowości Jawne interfejsy – zawsze wtedy, gdy moduły A i B komunikują się ze sobą, musi to być wyraźne uwidocznione w kodzie źródłowym A lub B, lub obu. Ukrywanie (hermetyzacja) informacji – projektant każdego modułu musi wybrać pewien podzbiór jego właściwości, który będzie stanowił oficjalną informację o tym module, udostępnianą twórcom modułów klienckich. Właściwości publiczne to właściwości eksportowane – interfejs modułu. Właściwości ukryte to właściwości prywatne.
27 Pięć zasad modułowości Zasada modułowych jednostek językowych – moduły muszą odpowiadać jednostkom składniowym występującym w stosowanym języku. Zasada samodokumentowania się kodu – projektant modułu powinien dążyć do tego, aby wszystkie informacje o module były zawarte w nim samym. Zasada jednolitego dostępu – wszystkie usługi oferowane przez moduł powinny być dostępne za pośrednictwem jednolitej notacji, która nie zdradza, czy zostały one zaimplementowane z wykorzystaniem danych przechowywanych statycznie, czy też obliczanych na bieżąco w razie potrzeby.
28 Pięć zasad modułowości Zasada otwarty-zamknięty – moduły powinny być jednocześnie otwarte i zamknięte. –Mówimy, że moduł jest otwarty, jeżeli można go rozszerzać. Na przykład powinna istnieć możliwość rozszerzania jego zbioru operacji lub dodania nowych pól do jego struktury. –Mówimy, że moduł jest zamknięty, jeżeli może być wykorzystywany przez inne moduły. Zakładamy tu więc, że ma on dobrze zdefiniowany, stabilny opis (interfejs zgodny z regułą ukrywania informacji). Zasada pojedynczego wyboru – zawsze wtedy, gdy program komputerowy musi obsługiwać jakiś zbiór alternatyw, ich pełna lista powinna być znana jednemu i tylko jednemu modułowi.
29 Cele wielokrotnego wykorzystania kodu Adekwatność czasowa – gdy korzystamy z gotowych komponentów, mamy mniej kodu do napisania, a tym samym możemy zrobić to szybciej Obniżenie kosztów konserwacji – gdy za oprogramowanie odpowiedzialny jest ktoś inny, również na niego spada obowiązek tworzenia nowych wersji. Pozwala to uniknąć paradoksu kompetentnego programisty – im więcej pracujesz, tym więcej masz do zrobienia, ponieważ użytkownicy twoich programów zaczynają prosić o rozszerzenie funkcjonalności, przeniesienie programu na inną platformę itd. Niezawodność – polegając na komponentach pochodzących z wiarygodnych źródeł mamy gwarancję, że autorzy tych komponentów podeszli do ich tworzenia w pełni profesjonalnie i poświęcili odpowiednią ilość czasu na ich dokładne przetestowanie.
30 Wydajność – twórcy komponentów stosują (a przynajmniej powinni) najlepsze znane algorytmy i struktury danych, ponieważ są to specjaliści z danej dziedziny. Natomiast w dużych projektach programistycznych trudno oczekiwać, aby w zespole byli sami eksperci, znający się na wszystkich zagadnieniach, o które otrze się tworzenie programu. Spójność – nie ma dobrej biblioteki, która nie zmuszałaby do stosowania przejrzystego, spójnego stylu projektowania aplikacji. Szczególnie styl niektórych najlepszych bibliotek obiektowych jest tak wyrazisty, że na zasadzie naturalnego procesu osmozy zaczyna wpływać na styl tworzenia aplikacji. Jest to bardzo istotny czynnik wyraźnie podnoszący jakość aplikacji tworzonych przez wieloosobowe grupy programistów. Inwestycja na przyszłość – tworzenie oprogramowania przystosowanego do wielokrotnego użycia jest sposobem na uwiecznienie doświadczenia i innowacyjnych pomysłów najlepszych programistów. Pozwala przekształcić delikatny i ulotny zasób w coś trwałego. Cele wielokrotnego wykorzystania kodu
31 Jakie elementy systemów nadają się do wielokrotnego wykorzystania? Personel Specyfikacje Projekty Wzorce projektowe Kody źródłowe Specjalistyczne komponenty Abstrakcyjne moduły
32 Problemy nietechniczne związane z wielokrotnym wykorzystaniem oprogramowania Syndrom NJTW (Nie Ja To Wymyśliłem) Syndrom NUN (Nawyk Unikania Nowatorstwa) Ekonomika przetargów Firmy programistyczne i ich strategie Dostęp do komponentów (indexing) Formaty dystrybucji komponentów wielokrotnego użytku
33 Wzorzec modułu has (t: TABLE, x: ELEMENT): BOOLEAN is -- Czy w t występuje x? local pos: POSITION do from pos := INITIAL_POSITION (x, t) until EXHAUSTED (pos, t) or else FOUND (pos, x, t) loop pos := NEXT (pos, x, t) end Result := not EXHAUSTED (pos, t) end
34 Kryteria obiektowości - kategorie Technika i język – są ściśle związane z procesem myślowym obecnym na etapie analizowania i projektowania oprogramowania oraz z notacją, którą się przy tym stosuje. Należy zwrócić uwagę na to, że (zwłaszcza w technice obiektowej) określenie „język” nie odnosi się jedynie do języka programowania w ścisłym tego słowa znaczeniu, ale też do notacji, mającej postać graficzną lub tekstową, którą stosuje się przy analizie i projektowaniu. Implementacja i środowiska – opisywane są podstawowe właściwości narzędzi, za pomocą których programiści wprowadzają w życie idee obiektowe. Biblioteki – technika obiektowa opiera się na wielokrotnym wykorzystywaniu gotowych komponentów programistycznych. Kryteria należące do tej kategorii odnoszą się zarówno do dostępności podstawowych bibliotek, jak i do mechanizmów niezbędnych do korzystania z nich i tworzenia nowych.
35 Kryteria obiektowości – Technika i język Jednolitość – zorientowany obiektowo język programowania, środowisko i wspierająca je technika, powinny mieć zastosowanie do całego cyklu życia aplikacji i to w sposób, który maksymalnie ułatwia przechodzenie między poszczególnymi czynnościami. Klasy – zarówno technika, jak i język powinny być zbudowane wokół pojęcia klasy. Asercje – język powinien dawać możliwość dołączania asercji (warunków początkowych, warunków końcowych i niezmienników) do klas i ich cech, musi opierać się na narzędziach służących do generowania dokumentacji na podstawie tych asercji i (opcjonalnie) pozwalać na monitorowanie asercji w trakcie wykonywania programów.
36 Kryteria obiektowości – Technika i język Klasy jako moduły – klasy powinny być jedynymi modułami, ponieważ obiektowość jest przede wszystkim techniką architektoniczną. Klasy jako typy – każdy typ powinien być oparty na jakiejś klasie. Klasy są same w sobie wystarczająco potężne, aby nie trzeba było już wprowadzać żadnego innego mechanizmu opisywania typów (INTEGER, REAL – klasy wbudowane). Sterowanie oparte na cechach – wywołania cech powinny być podstawowym mechanizmem sterującym. Wywołanie cech znane jest także jako przekazywanie komunikatów. Ukrywanie informacji – autor klasy powinien mieć możliwość zaznaczenia, czy dana cecha ma być dostępna dla wszystkich klientów, tylko dla wybranych, czy dla żadnego. Dobry, obiektowy język programowania nie powinien udostępniać żadnej postaci zmiennych globalnych.
37 Kryteria obiektowości – Technika i język Obsługa wyjątków – język powinien udostępniać mechanizm pozwalający na wychodzenie z niestandardowych sytuacji. Statyczna kontrola typów – dobrze zdefiniowany system kontroli typów powinien (przez narzucenie określonego zestawu reguł odnoszących się do deklarowania typów i gwarantujących zachowanie miedzy nimi zgodności) gwarantować bezpieczną obsługę typów w trakcie wykonywania programów, które zaakceptował. Reguły narzucające zgodność: –Każda encja (a więc każda nazwa odnosząca się w kodzie źródłowym do obiektów istniejących w czasie wykonywania programu) musi mieć jawnie określony typ, wywodzący się z jakiejś klasy; –Przy każdym wywołaniu na danej jednostce jakiejś cechy, uruchamiana jest cecha z klasy odpowiadającej tej encji (a cecha ta, z punktu widzenia ukrywania informacji, jest dostępna dla klasy obiektu wywołującego); –Przypisywanie i przetwarzanie argumentów musi podlegać regułom zgodności, opartym na dziedziczeniu, które wymagają, aby typ obiektu źródłowego był zgodny z typem obiektu docelowego.
38 Kryteria obiektowości – Technika i język Generyczność – powinno być możliwe pisanie klas z wykorzystaniem formalnych parametrów generycznych, reprezentujących dowolne typy. Ta postać parametryzowalności typów nazywana jest nieograniczoną generycznością. Jej uzupełnienie stanowi generyczność ograniczona, która jest związana z zagadnieniem dziedziczenia. Dziedziczenie pojedyncze – powinna istnieć możliwość zadeklarowania klasy jako potomka innej klasy. Dziedziczenie wielokrotne – każda klasa powinna mieć możliwość dziedziczenia z tylu innych klas, z ilu potrzeba. Powinien też istnieć mechanizm pozwalający radzić sobie z konfliktami nazw.
39 Kryteria obiektowości – Technika i język Dziedziczenie powtórne – powinien istnieć zbiór precyzyjnych reguł dotyczących cech nabywanych w wyniku wielokrotnego dziedziczenia, pozwalający programistom na ustalenie dla każdej powtórnie odziedziczonej cechy z osobna, czy ma być współużytkowana, czy replikowana. Ograniczona generyczność – mechanizm generyczności powinien obsługiwać jej ograniczoną postać. Połączenie generyczności i dziedziczenia pozwala zdefiniować klasę z generycznym parametrem, który nie reprezentuje dowolnego typu, lecz typ będący potomkiem danej klasy. Ponowna definicja – powinna istnieć możliwość ponownego zdefiniowania specyfikacji, sygnatury i implementacji dziedziczonej cechy. Polimorfizm – w trakcie działania programu powinna istnieć możliwość przypisywania encji (nazw w kodzie źródłowym, reprezentujących obiekty wykonywanego kodu) do obiektów różnych typów, aczkolwiek pozostająca pod nadzorem systemu kontroli typów uwzględniającego dziedzicznie.
40 Kryteria obiektowości – Technika i język Wiązanie dynamiczne – wywołanie cechy na jakiejś encji powinno zawsze powodować wywołanie tej jej wersji, jaka jest odpowiednia dla typu obiektu, który został dołączony do danej encji (a ten w czasie wykonywania programu może się zmieniać). Kontrola typów w trakcie wykonywania programu – w trakcie wykonywania programu powinna istnieć możliwość sprawdzenia, czy typ danego obiektu jest zgodny ze wskazanym typem. Klasy i cechy abstrakcyjne – powinna istnieć możliwość zadeklarowania klasy lub cechy jako abstrakcyjnej, czyli takiej, która jeszcze nie jest w pełni zaimplementowana. Zarządzanie pamięcią i mechanizm odzyskiwania pamięci – język powinien umożliwiać przeprowadzanie bezpiecznego, automatycznego zarządzania pamięcią, a implementacja powinna udostępniać automatyczny mechanizm przywracania pamięci.
41 Kryteria obiektowości – Implementacja i środowisko Automatyczne uaktualnianie – uaktualnianie programu po wprowadzeniu do niego zmian powinno odbywać się automatycznie. Analizowaniem zależności między klasami powinny zajmować się programy narzędziowe, a nie programiści. Szybkie uaktualnianie – czas potrzebny na przetworzenie zmian w programie i umożliwienie uruchomienia uaktualnionej wersji powinien zależeć jedynie od wielkości zmienianych komponentów, a nie od całkowitych rozmiarów aplikacji.
42 Kryteria obiektowości – Implementacja i środowisko Trwałość – powinien być dostępny mechanizm trwałego składowania obiektów, obsługujący pełną trwałość i umożliwiający przechowywanie na urządzeniach zewnętrznych wskazanych obiektów i wszystkich obiektów od nich zależnych. Obiekty te muszą dawać się wczytać zarówno w czasie tej samej sesji, jak i w kolejnych. Dokumentacja – powinny istnieć narzędzia pozwalające na automatyczne generowanie dokumentacji programów i klas. Przeglądanie – programy narzędziowe umożliwiające interaktywne przeglądanie klas powinny pozwalać programistom na szybkie i wygodne śledzenie zależności między klasami i cechami.
43 Kryteria obiektowości – Biblioteki Biblioteki podstawowe – powinien być dostępny zbiór klas przystosowanych do wielokrotnego użycia, implementujących najczęściej wykorzystywane struktury danych i algorytmy. Grafika i interfejs użytkownika – powinny istnieć klasy przystosowane do wielokrotnego użycia, umożliwiające tworzenie aplikacji o przyjemnym, graficznym interfejsie użytkownika. Mechanizmy rozwijania bibliotek – powinny istnieć mechanizmy pozwalające bibliotekom na ewoluowanie w sposób możliwie najmniej odbijający się na programach, które z nich korzystają. Mechanizmy katalogowania bibliotek – klasy biblioteczne powinny być zaopatrzone w informacje katalogowe umożliwiające wyszukiwanie ich po właściwościach.
44 Pięć wymagań dotyczących struktury modułu Zróżnicowanie typów – wzorzec podprogramu has zakłada istnienie tabeli zawierającej obiekty typu ELEMENT. Po przekształceniu wzorca w podprogram można doprecyzować, że typem tym ma być na przykład INTEGER lub BANK_ACCOUNT. To nie tak! Moduł przeszukiwawczy powinien sam z siebie obsługiwać elementy różnych typów. Za pomocą wzorca musimy tworzyć moduły o parametryzowalnych typach, moduły generyczne. Generyczność to mechanizm pozwalający na definiowanie parametryzowalnych wzorców modułów, których parametry reprezentują typy.
45 Pięć wymagań dotyczących struktury modułu Grupowanie podprogramów – sposób przeszukiwania tabeli zależy od tego, w jaki sposób została ona utworzona, jak wstawiane są do niej nowe elementy, jak są usuwane itd. Podprogram przeszukujący tabelę sam w sobie nie nadaje się jeszcze na zasób przystosowany do wielokrotnego użycia. Samowystarczalny moduł tego typu powinien mieć cały zestaw podprogramów realizujących powyższe operacje.
46 Pięć wymagań dotyczących struktury modułu Zróżnicowanie implementacji – wzorzec has jest bardzo ogólny. Pozwala on na stosowanie wielu różnych algorytmów i struktur danych. Ta różnorodność oznacza, że nie można oczekiwać, iż jeden moduł obsłuży wszystkie możliwości. Byłoby to trudne do osiągnięcia. Dlatego potrzeba całej rodziny modułów, przystosowanych do poszczególnych implementacji. Ogólna technika tworzenia i korzystania z modułów przystosowanych do wielokrotnego użycia musi to przewidywać.
47 Pięć wymagań dotyczących struktury modułu Niezależność reprezentacji – ogólna postać modułu powinna pozwalać klientom na określanie potrzebnych operacji bez konieczności znajomości ich implementacji. Przykład: Założenie – Moduł kliencki C wchodzący w skład aplikacji (np. program do zarządzania kapitałem, kompilator lub system informacji geograficznej) ma ustalić, czy określony element x występuje w tabeli t (zawierającej informacje o inwestycjach, słowa kluczowe lub nazwy miast). Realizacja – moduł C ma możliwość uzyskania informacji poprzez wywołanie present := has(t, x) Nie musi wiedzieć, jakiego rodzaju tabelą jest t (drzewem binarnym, tablicą mieszającą czy listą powiązaną). Autor modułu C musi jedynie wiedzieć, że t jest tabelą elementów określonego typu i x jest obiektem tego typu. Wybór algorytmu wyszukiwania, dostosowanego do implementacji t, jest zadaniem autora modułu zarządzającego tabelami. Chcemy, aby has automatycznie, w czasie wykonywania programu, dopasowywało się do tabeli t, nawet jeśli jej postać zmienia się z każdym wywołaniem.
48 Pięć wymagań dotyczących struktury modułu Wyodrębnianie wspólnych zachowań – moduł powinien być gotowy na wychwycenie wszelkich podobieństw, jakie mogą występować między implementacjami należącymi do tej samej rodziny. Różnorodność implementacji dostępnych dla niektórych problemów zazwyczaj będzie wymagała opracowania całej rodziny modułów. Wyzwanie polega tu na uniknięciu niepotrzebnego powielania operacji, to znaczy na oparciu działania modułu na operacjach wspólnych dla wszystkich wariantów.
49 Pięć wymagań dotyczących struktury modułu Wyodrębnianie wspólnych zachowań tabela tabela_sekwencyjna tabela_drzewiasta tabela_mieszająca tabela_plikowa tabela_powiązana tabela_tablicowa
50 Podział na podprogramy i pakiety Biblioteki podprogramów –Specyfikacja każdego problemu musi być prosta. Każdy problem musi dać się scharakteryzować za pomocą niewielkiego zbioru argumentów wejściowych i wyjściowych. –Problemy muszą wyraźnie różnić się od siebie. Wielokrotnie wykorzystuje się jakiś fragment projektu. –Nie mogą istnieć żadne złożone struktury danych (podprogramy korzystają z podprogramów). Pakiety –Pakiet jest strukturą językową, która ma swoją nazwę i jasno określony zakres. –Definicja każdego pakietu zawiera określoną liczbę deklaracji powiązanych ze sobą elementów, takich jak podprogramy i zmienne, nazywane cechami pakietu. –Każdy pakiet może precyzyjnie określać prawa dostępu, na podstawie których inne pakiety będą mogły korzystać z jego cech. Innymi słowy, mechanizm pakietów pozwala na ukrywanie informacji. –W językach kompilowanych każdy pakiet można kompilować niezależnie od pozostałych.
51 Przeciążanie Przeciążanie – to możliwość nadania określonej nazwie występującej w programie kilku różnych znaczeń. Najczęściej przeciąża się nazwy zmiennych. Ważniejsze jest przeciążenie podprogramów (operatorów – np. int a + int b, double a + double b), które pozwala stosować tą samą nazwę dla kilku podprogramów. has (t: „JAKIŚ_TYP_TABELI”, x: ELEMENT) Przeciążanie pozwala na stosowanie w kodzie źródłowym klienta tych samych zapisów przy stosowaniu różnych implementacji danego pojęcia.
52 Przeciążanie Znaczenie przeciążania Przeciążanie podprogramów jest możliwością stworzoną z myślą o klientach. Pozwala ono na stosowanie w kodzie źródłowym klienta tych samych zapisów przy stosowaniu różnych implementacji danego pojęcia. Przeciążanie składniowe (przeciążanie operatorów) Przeciążanie semantyczne (wiązanie dynamiczne)
53 Generyczność Generyczność to mechanizm pozwalający na definiowanie parametryzowalnych wzorców modułów, których parametry reprezentują typy. TABLE_HANDLING[G] – nazwa pakietu, gdzie G reprezentuje dowolny typ i jest tak zwanym generycznym parametrem formalnym. TABLE_HANDLING[INTEGER] TABLE_HANDLING[ACCOUNT] Np. INTEGER jest faktycznym parametrem generycznym, a proces uzyskiwania modułu ze wzorca nazywa się konkretyzacją generyczną. Generyczność pozwala na stosowanie w kodzie źródłowym dostawcy tych samych zapisów przy użyciu tych samych implementacji danego pojęcia, lecz odnoszących się do obiektów różnych typów.
54 Programowanie zorientowane obiektowo (1. definicja) Programowanie zorientowane obiektowo to sposób tworzenia oprogramowania, w którym architektura każdego systemu opierana jest na modułach wyprowadzonych z typów obiektów, na których system ten ma operować (a nie na funkcji bądź funkcjach, które mają się znaleźć w tym systemie). Motto obiektowości: Nie zaczynaj od pytania, co system ma robić, tylko na czym ma to robić!!!
55 Jak znajdować odpowiednie typy obiektowe? Wiele obiektów po prostu stanowi komputerowy model fizycznych obiektów znanych z codziennego życia – z dziedziny, do której odnosi się dany system. Źródłem typów obiektów mogą być też komponenty przystosowane do wielokrotnego użycia. Doświadczenie i naśladownictwo – czerpanie inspiracji z prac innych programistów.
56 Kryteria prawidłowego opisu obiektów Programowanie z dołu do góry. Opisy powinny być dokładne i jednoznaczne. Opisy powinny być wyczerpujące – przynajmniej na tyle, na ile jest to konieczne w danym przypadku. Opisy nie powinny być przesadnie szczegółowe.
57 Reprezentacje stosu
58 Abstrakcyjne postrzeganie obiektów Abstrakcja – w sztukach plastycznych: taka realizacja dzieła, w której jest ono pozbawione wszelkich cech ilustracyjności, a artysta nie stara się naśladować natury. Autorzy stosują różne środki wyrazu, dzięki którym "coś przedstawiają". Sztuka abstrakcyjna jest nazywana sztuką niefiguratywną. Jest to sztuka bardzo różnorodna, w której występują odrębne nurty, czasem związane z jednym tylko artystą. Abstrakcja (abstrahowanie) - proces tworzenia pojęć, w którym wychodząc od rzeczy jednostkowych (najczęściej konkretnych) dochodzi się do pojęcia bardziej ogólnego poprzez konstatowanie tego, co dla tych rzeczy wspólne (zazwyczaj właściwości). Ostatecznie drogą tego procesu dochodzi się do pojęć najuboższych w treść, ale o najszerszym zakresie, takich jak rzecz, przedmiot, substancja czy Byt. Pojęcie powstałe w wyniku procesu abstrakcji nazywamy abstraktem. Abstrakcją – w programowaniu nazywamy pewnego rodzaju uproszczenie rozpatrywanego problemu, polegające na ograniczeniu zakresu cech manipulowanych obiektów wyłącznie do cech kluczowych dla algorytmu, a jednocześnie niezależnych od implementacji. W tym sensie abstrakcja jest odmianą formalizmu matematycznego. Cel stosowania abstrakcji jest dwojaki: ułatwienie rozwiązania problemu i zwiększenie jego ogólności.
59 W przykładzie ze stosem, tym, co jednoczy wszystkie jego reprezentacje, niezależnie od różnic między nimi, jest fakt, że opisują one strukturę „pojemnikową” (strukturę, której przeznaczeniem jest przechowywanie innych obiektów), na której można wykonywać określone operacje i która ma pewne właściwości. Skupiając się nie na wyborze konkretnej reprezentacji, lecz na tych operacjach i właściwościach, możemy otrzymać abstrakcyjną, ale przydatną charakterystykę pojęcia stosu. Abstrakcyjne postrzeganie obiektów
60 Operacje, które zazwyczaj wykonuje się na stosie: polecenie służące do umieszczania elementu na wierzchołku stosu, put, polecenie służące do zdejmowania elementu z wierzchołka stosu, w przypadku gdy stos nie jest pusty, remove, zapytanie pozwalające sprawdzić, jaki element znajduje się na szczycie stosu, w przypadku gdy stos nie jest pusty, item, polecenie pozwalające sprawdzić, czy stos jest pusty (dzięki niemu klienci mogą zawczasu ustalić, czy wolno im wywołać polecenie remove lub item), empty, polecenie operacji tworzącej, która przygotuje (początkowo pusty stos), new. Abstrakcyjne postrzeganie obiektów
61 Specyfikacja abstrakcyjnego typu danych TYPES (typy) FUNCTIONS (funkcje) AXIOMS (aksjomaty) PRECONDITIONS (warunki początkowe)
62 Typ – jest to kolekcja obiektów charakteryzowanych przez funkcje, aksjomaty i warunki początkowe. Typ to zbiór obiektów. Typ STACK (stos) to zbiór wszystkich możliwych stosów, typ INTEGER to zbiór wszystkich możliwych wartości całkowitych, itd. TYPES – STACK [G] – specyfikacja dotyczy pojedynczego abstrakcyjnego typu danych STACK, opisującego stosy obiektów dowolnego typu G. G nazywamy generycznym parametrem formalnym abstrakcyjnego typu danych STACK, zaś o samym abstrakcyjnym typie danych STACK mówimy, że jest generyczny. Specyfikacja abstrakcyjnego typu danych
63 Funkcje – opisują wszystkie operacje, jakie można wykonać na egzemplarzach danego abstrakcyjnego typu danych. Są one podstawowym składnikiem każdej definicji typu. Opisują one egzemplarze typu nie przez pryzmat tego, czym one są, lecz tego, co mają do zaoferowania. FUNCTIONS –put: STACK [G] x G STACK [G] –remove: STACK [G] not STACK [G] –item: STACK [G] not G –empty: STACK [G] BOOLEAN –new: STACK [G] Specyfikacja abstrakcyjnego typu danych
64 AXIOMS (aksjomaty) – nie określa się wartości funkcji występujących w ATD, natomiast określa się właściwości tych wartości. Stąd wiadomo, że ATD określa np. stos STACK[G]. Dla każdego x: G, s: STACK[G], –A1, item(put(s,x)) = x –A2, remove(put(s,x)) = s –A3, empty(new) –A4, nie empty(put(s,x)) PRECONDITIONS – warunki początkowe określają dziedziny funkcji zdefiniowanych w ATD –remove(s: STACK[G]) require nie empty(s) –item(s: STACK[G]) require nie empty(s) Specyfikacja abstrakcyjnego typu danych
65 Definicja klasy Klasa jest to abstrakcyjny typ danych, wyposażony w implementacją, która może być częściowa. Klasę, która jest w pełni zaimplementowana, nazywamy konkretną. Klasę, która jest zaimplementowana tylko częściowo lub wcale, nazywamy abstrakcyjną. Każda klasa jest albo konkretna, albo abstrakcyjna.
66 Programowanie zorientowane obiektowo (2. definicja) Programowanie zorientowane obiektowo to technika tworzenia programów komputerowych, w której traktuje się je jako kolekcje (o ściśle określonej strukturze) implementacji (które mogą być częściowe) abstrakcyjnych typów danych.
67 Programowanie zorientowane obiektowo (2. definicja) Podstawą definicji jest pojęcie abstrakcyjnego typu danych. Do utworzenia programu są potrzebne nie tyle ATD, które są pojęciem matematycznym, co ich implementacje, należące do świata oprogramowania. Implementacje te nie muszą być kompletne. Wyrażenie „które mogą być częściowe” zostawia furtkę dla klas abstrakcyjnych, w tym dla ich skrajnej, całkowicie abstrakcyjnej postaci, w której nie mają one zaimplementowanych żadnych cech. System jest kolekcją klas, z których żadna nie jest szczególnie uprzywilejowana. Nie ma tu „góry” ani głównego podprogramu. Kolekcja ma ściśle określoną strukturę dzięki dwóm relacjom, jakie mogą zachodzić między klasami – dziedziczeniu i relacji dostawca-klient.
68 Klasa – struktura statyczna Klasa – to abstrakcyjny typ danych (ATD) wraz z całkowitą lub częściową implementacją. Klasa z pełną implementacją nazywana jest konkretną, a z niepełną – abstrakcyjną. Klasa jest typem – opisuje zbiór możliwych struktur danych, zwanych egzemplarzami klasy. ATD również posiadają egzemplarze. Różnica polega na tym, że egzemplarz ATD to element czysto matematyczny (element pewnego zbioru matematycznego), podczas gdy egzemplarz klasy to struktura danych, którą można przechowywać w pamięci komputera i manipulować nią w systemie oprogramowania. Produktem ubocznym definicji klasy jest definicja obiektu. Obiekt to po prostu egzemplarz jakiejś klasy. Na przykład egzemplarz klasy STACK reprezentujący pewien określony stos jest obiektem.
69 Klasa – struktura statyczna Klasa to kod programu. Jest ona statyczna – inaczej mówiąc – istnieje niezależnie od jakiegokolwiek działania. Z kolei obiekt pochodzący od tej klasy to dynamicznie stworzona struktura danych, istniejąca wyłącznie w pamięci komputera podczas działania systemu. Klasa jest modułem, czyli jednostką dekompozycji oprogramowania, ale także typem (albo w przypadku klasy o właściwościach generycznych, wzorcem typu). Treścią programów, które służą do tworzenia systemów (aplikacji) są klasy. Obiekty są pojęciem funkcjonującym jedynie w czasie wykonywania – oprogramowanie tworzy je i manipuluje nimi w czasie działania.
70 Jednolity system typów Reguła obiektowości Każdy obiekt jest egzemplarzem jakiejś klasy. Reguła obiektowości ma zastosowanie nie tylko do złożonych, zdefiniowanych przez programistę obiektów (takich jak struktury danych z kilkoma polami), ale do podstawowych obiektów, takich jak liczby całkowite, liczby rzeczywiste, wartości logiczne i znaki, które wszystkie będą traktowane jak egzemplarze predefiniowanych klas bibliotecznych (INTEGER, REAL, DOUBLE, BOOLEAN, CHARACTER).
71 Jednolity system typów Prosty i jednolity szkielet jest zawsze bardziej pożądany niż mnogość szczególnych przypadków. System typów w całości będzie opierał się na pojęciu klasy. Opisywanie podstawowych typów jako ATD, a tym samym jako klas jest proste i naturalne. Nie jest na przykład trudne dojście do tego, jak zdefiniować klasę INTEGER z cechami obejmującymi działania matematyczne, takie jak „+”, operacje porównania, takie jak „
72 Klasa POINT indexing description: „Punkty w przestrzeni dwuwymiarowej” class POINT feature x, y: REAL--Odcięta i rzędna rho: REAL is--Odległość od początku układu (0, 0) do Result := sqrt(x^2 + y^2) end theta: REAL is--Kąt tworzony z osią poziomą do … end distance(p: POINT): REAL is--Odległość do p do Result := sqrt((x – p.x)^2 + (y – p.y)^2) end
73 Klasa POINT translate(a, b: REAL) is --Przemieszcza poziomo o a i pionowo o b do x := x + a y := y + b end scale(factor: REAL) is--Skaluje według współczynnika factor do x := factor * x y := factor * y end rotate(p: POINT; angle: REAL) is --Obraca p wokół początku układu o kąt angle do … end
74 Klasa POINT Każdy abstrakcyjny typ danych, taki jak POINT, jest charakteryzowany przez zbiór funkcji, opisujący operacje mające zastosowanie do egzemplarzy ATD. W klasach (implementacjach ATD) funkcje są cechami – operacjami, które można stosować na egzemplarzach klasy. Są trzy rodzaje funkcji ATD: zapytania, polecenia i kreatory. Potrzebna jest podobna klasyfikacja cech, bazująca na tym, czy cecha jest implementowana przestrzenią, czy czasem. Cechy reprezentowane za pomocą przestrzeni – reprezentowane poprzez skojarzenie pewnej informacji z każdym egzemplarzem klasy. Takie cechy będą nazywane atrybutami. Dla punktów x i y to atrybuty w reprezentacji kartezjańskiej, a rho i theta to atrybuty w reprezentacji biegunowej. Cechy reprezentowane za pomocą czasu – reprezentowane poprzez zdefiniowanie jakiegoś obliczenia (algorytmu) obowiązującego dla wszystkich egzemplarzy klasy. Cechy te będą nazywane podprogramami. Dla punktów z klasy POINT rho i theta będą podprogramami w reprezentacji kartezjańskiej, a dla x i y podprogramami w reprezentacji biegunowej. Jeżeli podprogramy będą zwracać wynik nazwiemy je funkcjami. W klasie POINT zarówno x i y w reprezentacji biegunowej, jak rho i theta w reprezentacji kartezjańskiej są funkcjami, ponieważ wszystkie zwracają wynik typu REAL. Podprogramy, które nie zwracają wyników, odpowiadają poleceniom w specyfikacji ATD i nazywane są procedurami. Rozpatrywana klasa na przykład będzie zawierać procedury translate, rotate i scale.
75 Atrybuty i procedury Cecha PodprogramAtrybut ProceduraFunkcja Pamięć Obliczenia Brak wyniku Zwraca wynik p1.x
76 Bieżący egzemplarz - Current Kod klasy opisuje właściwości i zachowanie obiektów pewnego typu. Dokonuje tego, opisując właściwości i zachowanie typowego egzemplarza danego typu – bieżącego egzemplarza klasy. distance(p: POINT): REAL is--Odległość do p do if p /= Current then Result := sqrt((x – p.x)^2 + (y – p.y)^2) end Najczęściej egzemplarz bieżący jest niejawnie obowiązujący i nie trzeba odwoływać się do egzemplarza Current poprzez jego nazwę. Ale kim tak naprawdę jest Current?
77 Klienci i dostawcy W czystej postaci podejścia obiektowego, każdy element kodu przynależy do jakiejś klasy. Są dwa sposoby użycia klasy POINT: odziedziczenie klasy lub zostanie klientem tej klasy. Definicja klienta, dostawcy Niech S będzie klasą. Klasa C, która zawiera deklarację w postaci a:S, jest określana mianem klienta S. Klasa S staje się wówczas dostawcą klasy C.
78 Klienci i dostawcy Przykład: -deklaracje x, y, rho, theta i distance czynią z klasy POINT klienta klasy REAL -inne klasy mogą z kolei stać się klientami POINT class GRAPHICS feature p1: POINT … some_routine is -- Wykonuje pewne działania na p1 do … Tworzy egzemplarz klasy POINT i związuje go z p1 … p1.translate(4.0, -1.5) … end … end
79 Wywołanie cechy Przykład wywołania cechy na rzecz obiektu p1.translate(4.0, -1.5) jest podstawowym mechanizmem obiektowego działania programów. p1 jest celem wywołania cechy. Skutek wywołania cechy f na obiekcie docelowym x Skutkiem wywołania jest zastosowanie cechy f na obiekcie związanym z x po zainicjalizowaniu każdego z formalnych argumentów f (jeżeli takie występują) wartościami odpowiadających im argumentów rzeczywistych. x.f x.f(u, v, …)
80 Zasada pojedynczego celu translate(p1, 4.0, -1.5) – wszystkie argumenty są traktowane tak samo W obiektowej formie wywołania nie obserwuje się takiej symetrii – wybieramy pewien obiekt (p1) jako cel wywołania, a pozostałe argumenty (4.0, -1.5) pełnią jedynie rolę pomocniczego rzutowania. Reguła pojedynczego celu Każda operacja w ramach obiektowego przebiegu wykonania odnosi się do jakiegoś obiektu, który jest bieżącym egzemplarzem w czasie wykonywania tej operacji
81 Tożsamość modułów i typów Zależność między modułami a typami Możliwości udostępnione przez klasę POINT postrzeganą jako moduł to operacje, jakie można wykonywać na egzemplarzach klasy POINT postrzeganej jako typ. Zasada wywołania cechy F1 – Elementy programu są wykonywane tylko w ramach wywołania cechy F2 – Każde wywołanie odnosi się do jakiegoś obiektu docelowego
82 Eksportowanie wybiórcze a ukrywanie informacji Pełna jawność: class S1 feature f … g … … end cechy f, g, … są dostępne dla wszystkich klientów S1.
83 Eksportowanie wybiórcze a ukrywanie informacji Zawężanie możliwości dostępu: class S2 feature f … g … feature {A, B} h … … end cechy f, g, … są dostępne dla wszystkich klientów S2, natomiast cecha h jest dostępna tylko dla klas A i B oraz ich potomków (klas, które dziedziczą pośrednio lub bezpośrednio po klasie A lub B).
84 Eksportowanie wybiórcze a ukrywanie informacji Całkowite ukrywanie cech klasy: class S3 feature { } i … … end cecha i jest ukryta przed wszystkimi klientami S3. Można ją zadeklarować jako eksportowaną do pustej listy klientów.
85 Wieki Wybuch - oznaczenia Zasada wywołania cechy Elementy programu są wykonywane tylko w ramach wywołania cechy. Każde wywołanie odnosi się do jakiegoś obiektu docelowego. Każde wywołanie będzie miało jedno z następujących dwóch postaci: –niekwalifikowane – f(a, b, …) –kwalifikowane – x.g(u, v, …) Wywołanie występuje w treści podprogramu r. Jego wykonanie jest możliwe tylko jako element wywołania r. Załóżmy, że znamy obiekt docelowy wywołania, którym jest pewien obiekt OBJ (obiekt docelowy bieżącego wywołania!!!). Wówczas obiekt docelowy t jest w każdym przypadku łatwy do ustalenia.
86 Wieki Wybuch - względność T1 – Dla postaci niekwalifikowanej t to po prostu OBJ. Przypadki T2, T3 i T4 odnoszą się do postaci kwalifikowanej. T2 – Jeżeli x jest atrybutem, to pole x obiektu OBJ ma wartość, która musi zostać związana z jakimś obiektem – obiektem tym jest właśnie t. T3 – Jeżeli x jest funkcją, to musimy najpierw wykonać (niekwalifikowane) wywołanie x. Funkcja zwróci t. T4 – Jeżeli x jest lokalną encją r, to wcześniejsze instrukcje nadały wartość x, która w chwili wywołania musi zostać związana z jakimś obiektem – obiektem tym jest właśnie t.
87 Wieki Wybuch - początek Definicja wykonania systemu Wykonanie obiektowego systemu oprogramowania składa się z następujących dwóch kroków: utworzenia pewnego obiektu, zwanego obiektem głównym dla danego wykonania, zastosowania na tym obiekcie określonej procedury, zwanej procedurą tworzącą. W chwili Wielkiego Wybuchu tworzone są obiekty i uruchamiana jest procedura tworząca. Obiekt główny jest egzemplarzem pewnej klasy, która jest klasą główną systemu. Procedura tworząca jest jedną z procedur klasy głównej. W większości systemów procedura tworząca sama wykreuje nowe obiekty i wywoła na nich procedury, wyzwalając kolejne utworzenia obiektów i kolejne wywołania. „Pokaz sztucznych ogni zaczynający się od jednej małej iskry”.
88 Wieki Wybuch - Current Pierwszym obiektem Current, od którego wszystko się zaczyna, jest obiekt główny. Na dowolnym etapie wykonania systemu, niech r będzie ostatnio wywołanym podprogramem. Jeżeli OBJ był bieżącym obiektem w chwili wywołania r, oto czym staje się Current: Jeżeli r wykonuje instrukcję, która nie wywołuje podprogramu (na przykład przypisanie), to obiektem bieżącym pozostaje ten sam obiekt. Zainicjowanie wywołania niekwalifikowanego również pozostawia ten sam obiekt obiektem bieżącym. Zainicjowanie kwalifikowanego wywołania x.f … sprawia, że obiekt docelowy tego wywołania, czyli obiekt związany z x, staje się nowym obiektem bieżącym. Po zakończeniu wywołania rolę obiektu Current ponownie przejmuje OBJ. W 2 i 3 przypadku wywołanie może odnosić się do podprogramu, który sam w sobie zawiera dalsze wywołania. Reguły te należy więc interpretować rekurencyjnie.
89 Systemy klas = gotowy do wykonania kod Aby stworzyć system klas potrzebujemy trzech elementów: zbioru klas CS, zwanego zbiorem klas systemu obrania którejś klasy ze zbioru CS jako klasy głównej wskazania procedury w klasie głównej, która jest procedurą tworzącą obiekt główny. Definicja systemu zamkniętego System jest zamknięty wtedy i tylko wtedy, jeżeli zbiór jego klas obejmuje wszystkie klasy, których potrzebuje klasa główna.
90 Definicja encji Encje obejmują nazwy, które oznaczają wartości istniejące w czasie wykonania, związane z możliwymi obiektami. Encja jest jednym z następujących elementów: atrybutem klasy, lokalną encją podprogramu, w tym także predefiniowaną encją Result występującą w funkcji, formalnym argumentem podprogramu.
91 Struktury czasu wykonywania: obiekty W każdym momencie działania, system zorientowany obiektowo będzie posiadać pewną liczbę obiektów. Struktura czasu wykonywania to sposób ich zorganizowania i relacje między nimi. Definicja obiektu Obiekt jest egzemplarzem klasy w czasie wykonywania. Program zawierający klasę C w różnych momentach swojego działania może tworzyć (poprzez operacje tworzenia i klonowania) egzemplarze C. Taki egzemplarz jest strukturą zbudowaną według wzorca zdefiniowanego przez C. Obiekt posiada także bardziej ogólne znaczenie, pochodzące z języka potocznego. Każdy system informatyczny jest powiązany z jakimś systemem zewnętrznym, który może zawierać „obiekty”: punkty, linie, kąty, powierzchnie bądź bryły, jeśli rozważamy system graficzny; pracowników, czeki i pensje w systemie płacowym itd.
92 Struktury czasu wykonywania: obiekty Niech O będzie obiektem. Zgodnie z definicją obiektu wiemy, że jest to egzemplarz jakiejś klasy, a mówiąc bardziej precyzyjnie, można stwierdzić, że jest to bezpośredni egzemplarz konkretnej klasy, np. C. Klasa C jest nazywana klasą generującą lub po prostu generatorem O. Klasa C jest fragmentem kodu programu, natomiast O jest strukturą czasu wykonywania. Wśród swoich cech C posiada pewną liczbę atrybutów. Te atrybuty całkowicie określają postać obiektu – O jest po prostu kolekcją komponentów lub pól, po jednym na każdy atrybut. (rys. 1)
93 Pola proste BOOLEAN, CHARCTER, INTEGER, REAL, DOUBLE, STRING class ksiazka feature tytul: STRING data, liczba_stron: INTEGER end class autor feature imie, nazwisko: STRING rok_urodzenia: INTEGER end(rys. 2, 3 i 4) Obiekty, których pola to wyłącznie typy proste nie dają zbyt wielu możliwości.
94 Obiekt podrzędny Rozważmy zbiór obiektów klasy ksiazka, w którym pole autor jest konkretnym obiektem z klasy autor. (Rys. 4) Takie powielenie jest niedopuszczalne: Niepotrzebnie zajmuje pamięć. Np. wyobraźmy sobie zbiór obiektów reprezentujących ludzi, przy czym każdy z tych obiektów posiadałby obiekt podrzędny oznaczający kraj, którego obywatelem jest dana osoba, a wszystko to przy założeniu, że liczba ludzi jest duża, natomiast liczba krajów niewielka. Taka technika nie pozwala na współużytkowanie. Niezależnie od wyboru reprezentacji, pola autor odnoszą się do tego samego egzemplarza klasy autor; jeśli obiekt autor zostanie zaktualizowany, zmiana ta powinna być widoczna we wszystkich obiektach oznaczających książki napisane przez tę osobę.
95 Definicja referencji Referencja jest wartością czasu wykonywania, może ona być pusta bądź związana. Referencja, jeśli jest związana, identyfikuje pojedynczy obiekt (mówi się wówczas, że jest związana z tym konkretnym obiektem). (rys. 5)
96 Tożsamość obiektów Każdy obiekt utworzony w czasie wykonywania programu posiada unikalną tożsamość, niezależną od wartości przechowywanych przez pola tego obiektu. Dwa obiekty o różnej tożsamości mogą mieć identyczne pola. I na odwrót – pola mogą się zmieniać podczas działania programu, ale nie wpływa to na tożsamość obiektu.
97 Tożsamość obiektów A oznacza ten sam obiekt co B może być niejednoznaczne. Czy myślimy o obiektach posiadającą różną tożsamość, ale takie same pola? A może mamy na myśli ten sam obiekt, ale przed zmianami oraz po zmianach mających wpływ na jego pola?
98 Deklarowanie referencji class Ksiazka feature tytul: STRING data, liczba_stron: INTEGER autor: Autor end class Autor feature imie, nazwisko: STRING rok_urodzenia, rok_śmierci: INTEGER end
99 Autoreferencja Dobrze class Osoba feature imie: STRING ukochana/y, gospodarz: Osoba end Źle class Osoba1 feature mama, tata: Osoba1 end
100 Struktura obiektów w czasie wykonywania System jest złożony z pewnej liczby obiektów o różnych polach Niektóre z tych pól to wartości należące do typów prostych Inne są referencjami, z których część jest pusta, a część związana z obiektami Każdy obiekt jest egzemplarzem określonego typu Niektóre typy mogą posiadać tylko jeden egzemplarz (np. typ2, typ3, typ4 i typ5) Częściej obecnych jest wiele egzemplarzy jednego typu (typ1) Obiekt może posiadać wyłącznie pola z referencjami (typ4) Może posiadać same pola typów prostych (typ5) Istnieją też referencje wskazujące na siebie: bezpośrednie (typ2) pośrednie – cykl referencyjny zaczynający się i kończący na egzemplarzu typ1 (patrz zgodnie z ruchem wskazówek zegara)
101 Obiekty jako narzędzie do modelowania Cztery światy w tworzeniu oprogramowania Modelowany system, nazywany także systemem zewnętrznym (w przeciwieństwie do systemu informatycznego), opisany za pomocą typów obiektów i abstrakcyjnych relacji między nimi. Konkretny egzemplarz systemu zewnętrznego złożony z obiektów połączonych relacjami. Oprogramowanie składające się z klas połączonych relacjami w zgodzie z metodą obiektową (z wykorzystaniem klas klientów i dziedziczenia). Struktura obiektowa, która może istnieć podczas wykonywania programu; złożona z obiektów połączonych referencjami.
102 Obiekty jako narzędzie do modelowania Abstrakcyjny typ danych Klasa Obiekt modelu Obiekt programistyczny FORMAINSTANCJA ABSTRAKCJA KONKRET jest instancją implementuje
103 Dynamiczne tworzenie obiektów Instrukcja tworząca class QUOTATION feature source: BOOK page: INTEGER make_book is -- Utwórz obiekt klasy BOOK i powiąż go z source do !! source end source jest atrybutem otaczającej klasy lub lokalnym bytem otaczającego podprogramu. Jest też nazywane celem instrukcji tworzącej.
104 Działanie podstawowej instrukcji tworzącej Wynikiem instrukcji tworzącej w postaci !! x, gdzie typ celu x jest typem referencyjnym bazującym na klasie C, będzie wykonanie trzech następujących kroków: Utwórz nowy egzemplarz C (jako kolekcję pól, po jednym na każdy atrybut C). Oznacz nowy egzemplarz jako OC. Zainicjalizuj każde pole OC standardowymi wartościami domyślnymi. Przypisz wartość x (referencję) do OC. Domyślne wartości inicjujące: dla referencji domyślną wartością jest pusta referencja, dla BOOLEAN domyślną wartością jest False, dla CHARACTER domyślną wartością jest pusty znak (null), dla liczb (typu INTEGER, REAL lub DOUBLE) domyślną wartością jest zero (wartość zerowa o odpowiednim typie)
105 Procedury tworzące indexing … class C creation p1, p2, … feature … Deklaracja cech, w tym deklaracje procedur p1, p2, … end Instrukcja !! x zostaje zastąpiona następującą formą !! x.p1(…), !! x.p2(…), … gdzie p1 to jedna z procedur tworzących wymienionych w klauzuli creation, zaś (…) to poprawna lista rzeczywistych argumentów dla p. Efektem działania takiej instrukcji będzie utworzenie obiektu z wartościami domyślnymi, a następnie zastosowanie do wyniku procedury p i jej argumentów. Cała instrukcja jest określana mianem wywołania tworzącego.
106 Skutek wywołania tworzącego Wynikiem wywołania tworzącego !! x.p(…), gdzie typ celu x jest typem referencyjnym bazującym na klasie C, p jest procedurą tworzącą klasy C, a (…) reprezentuje poprawną listę rzeczywistych argumentów tej procedury (gdy są one konieczne), będzie wykonanie czterech następujących kroków. Utwórz nowy egzemplarz C (jako kolekcję pól, po jednym na każdy atrybut C). Oznacz nowy egzemplarz jako OC. Zainicjalizuj każde pole OC standardowymi wartościami domyślnymi. Przypisz wartości x (referencję) do OC. Wywołaj procedurę p z podanymi argumentami w kontekście obiektu OC. !! my_point.make_cartesian(0, 1) !! my_point.make_polar(1, Pi/2)
107 Stany referencji STAN PUSTY STAN ZWIĄZANY !! b b := c (gdzie c jest związane) b := Void b := c (gdzie c jest puste)
108 Operacje na referencjach class PERSON feature name: STRING person_1, person_2: PERSON set_loved (l, PERSON) is -- Zwiąż pole person_1 bieżącego obiektu z l do person_1 := l end Procedura set_loved przypisuje polu person_1 bieżącego egzemplarza PERSON wartość innej referencji, l. Przypisania referencji wykorzystują symbol :=, po którego prawej stronie umieszcza się źródło przypisania, a po prawej – cel. W przykładzie źródło i cel są referencjami, więc takie przypisanie nazywa się przypisaniem referencji.
109 Operacje na referencjach Tak jak można posłużyć się operacją do wiązania referencji z obiektem (przypisanie z użyciem := ), należy mieć możliwość sprawdzenia czy dwie referencje wskazują na ten sam obiekt. W tym celu wykorzystuje się zwykły operator równości =. Jeżli a i b są encjami typu referencyjnego, to wyrażenie: a = b będzie prawdziwe wtedy i tylko wtedy, gdy obie referencje są jednocześnie puste bądź jednocześnie związane z tym samym obiektem.
110 Operacje na referencjach Klonowanie można osiągnąć za pomocą wywołania funkcji clone. Jeśli y jest związane z obiektem OY, wyrażenie x := clone(y) wskaże nowy obiekt OX, który będzie mieć takie same pola jak OY, a każde z nich będzie identyczne z odpowiadającym mu polem obiektu OY. Jeśli y jest puste, wówczas wartość clone(y) także będzie pusta. Wywołanie equal(x, y) zwróci wartość logiczną True wtedy i tylko wtedy, gdy x i y są jednocześnie puste lub związane z dwoma obiektami, których odpowiadające sobie pola przechowują te same wartości.
111 Operacje na referencjach Funkcja clone tworzy nowy obiekt, będący wierną kopią już istniejącego. Mając już obiekt docelowy, można nadpisać jego pola. Umożliwia to procedura copy. Wywołuje ją instrukcja: x.copy(y) gdzie x i y muszą być tego samego typu; wynikiem instrukcji jest skopiowanie pól obiektu związanego z y do obiektu związanego z x. Tak jak wszystkie wywołania procedur i właściwości, również copy wymaga, by cel x nie był pusty. Co więcej, y także nie może być puste. Niemożność operowania na pustych wartościach odróżnia copy od clone.
112 Obiekty złożone i typy rozszerzone W ramach idei jednolitego systemu, typy proste (REAL, INTEGER, …) są obsługiwane dokładnie tak samo jak typy zdefiniowane przez programistę (POINT, BOOK, …). Jeżeli używamy n do manipulowania liczbą całkowitą, chcemy by n było tą liczbą, a nie referencją do obiektu przechowującego wartość n. Potrzebne są encje, które nie są referencjami do obiektów, ale których wartościami są obiekty. Główny powód to wydajność – byłaby potrzebna duża ilość czasu i miejsca, gdyby wszystkie operacje na liczbach całkowitych były niebezpośrednie. Innym powodem jest kwestia dokładnego modelowania – liczba całkowita to nie to samo, co referencja do niej.
113 Obiekty złożone i typy rozszerzone Niech C będzie klasą zadeklarowaną w ten sposób: class C feature … end C można używać jako typu. Encja o zadeklarowanym typie C reprezentuje referencję. Z tego powodu C określa się jako typ referencyjny. Potrzebna jest encja x, której wartość podczas wykonywania będzie egzemplarzem C, a nie referencją do takiego egzemplarza. Uzyskuje się to deklarując x jako: x : expanded C Wyrażenie expanded C oznacza typ. Egzemplarze tego typu są dokładnie takie same jak egzemplarze C. Jednak encja typu C oznacza referencję, która może być związana z egzemplarzem C, zaś encja typu expanded C oznacza bezpośredni egzemplarz C.
114 Obiekty złożone i typy rozszerzone ref sub COMPOSITE (obiekt złożony) Obiekt O nazywamy złożonym, jeśli jedno lub więcej jego pól to obiekty – nazywa się je obiektami podrzędnymi O. class COMPOSITE feature ref: C sub: expanded C end
115 Obiekty złożone i typy rozszerzone ref sub COMPOSITE (obiekt złożony) Klasa COMPOSITE jest zależna od zadeklarowanej wcześniej klasy C. Posiada dwa atrybuty: ref, oznaczający referencję oraz sub, który oznacza obiekt. To właśnie sub sprawia, że klasa jest złożona. Pole ref jest referencją związaną z egzemplarzem C bądź puste. Pole sub, które nie może być puste, zawiera egzemplarz C.
116 Obiekty złożone i typy rozszerzone Można napisać klasę E w taki sposób, by wszystkie encje typu E były rozszerzone. Taką klasę definiuje się następująco: expanded class E feature …deklaracje i definicje jak w przypadku zwykłej klasy… end W ten sposób uzyskuje się klasę rozszerzoną. Encje typu E będą oznaczać obiekty, a nie referencje. Typ, który nie jest rozszerzony jest typem referencyjnym.
117 Obiekty złożone i typy rozszerzone Rola typów rozszerzonych: 1.poprawiają efektywność 2.pozwalają na lepsze modelowanie 3.umożliwiają obsługę typów prostych w jednolity, zorientowany obiektowo sposób 1. Bez typów rozszerzonych trzeba by zawsze używać referencji do opisu obiektów złożonych. Uzyskanie dostępu do obiektów podrzędnych wymagałoby dodatkowej operacji – zwanej „dereferencją” – co wiąże się z narzutem czasowym. Pojawia się też narzut przestrzeni, ponieważ struktura czasu wykonywania musi poświęcić dodatkowe miejsce na same referencje.
118 Obiekty złożone i typy rozszerzone 2. Programowanie obiektowe jest techniką modelowania, dlatego zachodzi potrzeba, by oddzielnie modelować obiekty złożone oraz obiekty zawierające referencje do innych obiektów. Jest to zagadnienie konceptualne, a nie implementacyjne. Dwie deklaracje atrybutów w klasie C (S jest klasą referencyjną): D1 – ref: S D2 – exp: expanded S Deklaracja D1 oznacza, że każdy egzemplarz C „wie” o pewnym egzemplarzu S. Deklaracja D2 stwierdza, że każdy egzemplarz C „zawiera” egzemplarz S. Relacja „zawiera” obecna w typach rozszerzonych nie pozwala na współużytkowanie wewnętrznych cech, natomiast relacja „wie” umożliwia, by dwie lub więcej referencji było związanych z tym samym obiektem.
119 Obiekty złożone i typy rozszerzone class WORKSTATION feature k: expanded KEYBOARD c: expanded CPU m: expanded MONITOR n: NETWORK … end Definicja klasy odzwierciedla właściwości jej komponentów przez użycie typów rozszerzonych dla pierwszych trzech atrybutów oraz typu referencyjnego dla atrybutu odpowiadającego sieci. Pojęcie typu rozszerzonego, które w pierwszej chwili wydaje się być związane jedynie z implementacją, faktycznie pomaga opisać pewne relacje na etapie modelowania informacji. Relacja „zawiera” i jej przeciwieństwo, czyli relacja nazywana „jest częścią”, są podstawowe z punktu widzenia modelowania systemów zewnętrznych. Pojawiają się zarówno w metodach analitycznych, jak i modelowaniu bazodanowym.
120 Obiekty złożone i typy rozszerzone Agregacja – każdy obiekt pewnego typu jest kombinacją (agregatem) zera lub kilku obiektów określonych typów. Można np. zdefiniować „samochód” jako agregację „silnika”, „karoserii”, „skrzyni biegów” itd. class CAR feature e: expanded ENGINE b: expanded BODY … end Dana klasa C jest rozszerzonym klientem danej klasy S, jeśli posiada deklarację cechy typu expanded S (lub tylko S, jeśli S jest typu rozszerzoanego). Stosując podejście obiektowe, możemy uniknąć mnożenia się relacji, ponieważ wystarczą tylko dwie z nich: kliencka (rozszerzona lub nie) oraz relacja dziedziczenia.
121 Generyczność LIST_OF_BOOKS (lista książek) LIST_OF_JOURNALS (lista czasopism) LIST_OF_PEOPLE (lista osób) LINKED_LIST_OF_BOOKS (powiązana lista książek) SET_OF_BOOKS (zbiór książek) Abstrakcja Specjalizacja Parametryzacja typu
122 Generyczność Klasa LIST_OF_BOOKS reprezentuje listę obiektów klasy BOOK. Posiada cechy put, remove, count, … itd. Listy są specjalnym przypadkiem struktury „pojemnika”, której innymi przykładami są też między innymi drzewa, stosy i tablice. Wariant bardziej ogólny mogłaby implementować klasa SET_OF_BOOKS. Wariant bardziej szczegółowy, w którym miałby być zastosowany jakiś konkretny sposób reprezentacji listy, mogłaby reprezentować klasa LINKED_LIST_OF_BOOKS. Właśnie tego dotyczy pionowy kierunek zaznaczony na rysunku – wymiar dziedziczenia. Listy książek są szczególnym przypadkiem list obiektów jednego, konkretnego typu. Inne przykłady list tego typu to listy czasopism, ludzi, liczb całkowitych, itd. To poziomy wymiar rysunku – wymiar generyczności. Wyposażając klasy w parametry reprezentujące dowolne typy, unikniemy potrzeby pisania wielu prawie identycznych klas, takich jak LIST_OF_BOOKS, LIST_OF_PEOPLE.
123 Generyczność Przykład generycznego abstrakcyjnego typu danych: STACK [G] G – występujące w specyfikacji abstrakcyjnego typu danych reprezentuje każdy możliwy typ elementów. Jest to generyczny parametr formalny klasy. Stosując go, można zapisać uniwersalną deklarację wszystkich możliwych stosów, a nie osobnych klas INTEGER_STACK, REAL_STACK, itd. Każdy ATD opisujący struktury-pojemniki, czyli struktury danych, takie jak zbiory, listy, drzewa, macierze, tablice, itp. jest w podobny sposób generyczny.
124 Generyczność Przykład stosu INTEGER_STACK: put(element: INTEGER) is -- umieść element na wierzchołku stosu do … end item: INTEGER is -- element „wierzchołkowy” do … end Wszystkie wystąpienia INTEGER wynikają z reguły jawnego deklarowania typów. W konsekwencji trzeba by tworzyć osobną klasę dla każdego możliwego rodzaju stosu: INTEGER_STACK, REAL_STACK, POINT_STACK, BOOK_STACK, … Klasy-pojemniki przeciwstawiają sobie dwa podstawowe cele decydujące o jakości programowania zorientowanego obiektowo: niezawodność – zachowanie bezpieczeństwa typów poprzez wymóg jawnego ich deklarowania, przystosowanie do wielokrotnego użycia – możliwość napisania niewielkiego fragmentu kodu obejmującego wiele wariantów danego zagadnienia.
125 Generyczność Przy budowaniu klas generycznych dąży się do pogodzenia statycznej kontroli typów z wymogiem przystosowania do wielokrotnego użycia klas opisujących struktury pojemnikowe. Oznacza to, że chcemy: deklarować typy wszystkich encji występujących w kodzie źródłowym klasy obsługującej np. stos, w tym encji reprezentujących elementy stosu, napisać klasę w taki sposób, aby nie zdradzała typu obsługiwanych przez nią elementów, a tym samym można ją było wykorzystywać do tworzenia np. stosów dowolnych elementów. Pierwszy z punktów zmusza, co prawda, do zadeklarowania typu, ale nigdzie nie jest napisane, że musi to być deklaracja dokładna! Wystarczy, że dostarczymy jego nazwę! Stąd idea generyczności – aby otrzymać parametryzowalną klasę, umieść w jej deklaracji nazwę fikcyjnego typu, nazywanego generycznym parametrem formalnym.
126 Generyczność indexing description: „Stosy elementów dowolnego typu G” class STACK [G] feature count: INTEGER -- Liczba elementów na stosie empty: BOOLEAN is – Czy stos jest pusty? do … end full: BOOLEAN is -- Czy stos jest pełny? do … end item: G is -- Element wierzchołkowy do … end put (x: G) is -- Umieść x na wierzchołku do … end remove is -- Usuń element wierzchołkowy do … end end
127 Korzystanie z klasy generycznej Klient może wykorzystać klasę generyczną do zadeklarowania własnych encji, takich jak encje reprezentujące stos. Wtedy w deklaracji muszą zostać wymienione typy, zwane faktycznymi parametrami generycznymi, w liczbie odpowiadającej liczbie generycznych parametrów formalnych występujących w definicji klasy. sp: STACK [POINT] slp: STACK [LIST [POINT]] ssp: STACK [STACK [POINT]] Dostarczenie faktycznego parametru generycznego do klasy generycznej w celu stworzenia konkretnego typu, nazywane jest konkretyzacją klasy generycznej. Konkretyzacja generyczna tworzy nowy typ, ale i wymaga dostarczenia: wyniku konkretyzacji, w tym przypadku STACK [POINT], który jest typem, istniejącego typu (w tym przypadku POINT), który będzie pełnił rolę faktycznego parametru generycznego, aby uzyskać ten wynik.
128 Operacje na encjach klas generycznych Weźmy pod uwagę encję zdefiniowaną w klasie generycznej C[G, H, …], której typ jest jednym z generycznych parametrów formalnych, na przykład element x typu G. Gdy klasa ta jest wykorzystywana przez klienta do deklarowania encji, G może reprezentować dowolny typ, a więc każda operacja, którą podprogramy C wykonują na x musi dawać się zastosować dla wszystkich typów. Dozwolone zastosowania encji x to: Użycie x po lewej stronie przypisania x := y, gdzie wyrażenie występujące po prawej stronie, y, również jest typu G Użycie x po prawej stronie przypisania y := x, gdzie encja występująca po lewej stronie, y, również jest typu G Użycie x w wyrażeniu logicznym postaci x = y lub x /= y, gdzie y również jest typu G Użycie x w charakterze faktycznego argumentu w wywołaniu programu, zgodnego z argumentem formalnym, którego zadeklarowanym typem jest G lub ANY (klasa zawierająca cechy dziedziczone przez wszystkie inne klasy). Niezależnie od tego, jaki faktyczny typ będzie reprezentował G w danej konkretyzacji generycznej, element x będzie miał dostęp do wszystkich cech (clone, copy, equal, …) Użycie x jako celu wywołania jakiejkolwiek cechy ANY.
129 Dziedziczenie Motto: Ciekawe programy rzadko rodzą się w pustym świecie. Niemal zawsze nowe oprogramowanie jest rozszerzeniem już istniejącego. Najlepszą metodą tworzenia jest naśladowanie, ulepszanie i łączenie.
130 Dziedziczenie Cel: biblioteka graficzna, opisująca abstrakcje geometryczne – punkty, odcinki, wektory, okręgi, elipsy, wielokąty, trójkąty, prostokąty, kwadraty, itd. indexing opis: „Wielokąty o zadanej liczbie wierzchołków” class POLYGON creation feature -- Dostęp count: INTEGER -- Liczba wierzchołków perimeter: REAL is -- Długość obwodu do … end feature -- Transformacja display is -- Wyświetl wielokąt na ekranie do … end rotate (center: POINT, angle: REAL) is -- Obróć o kąt angle wokół punktu center do … end translate (a, b: REAL) is -- Przesuń w poziomie o a i w pionie o b do … end feature -- Implementacja vertices: LINKED_LIST [POINT] invariant -- Wielokąt ma przynajmniej trzy wierzchołki count = vertices.count count >= 3 end
131 Dziedziczenie Cel: potrzebna jest klasa prostokąt – cechy wspólne: np. przesuwanie, wyświetlanie, obracanie; - cechy charakterystyczne: np. przekątna, dokładnie cztery wierzchołki, kąty proste, łatwiejszy sposób liczenia obwodu indexing opis: „Prostokąty jako specjalna odmiana wielokątów” class RECTANGLE inherit POLYGON redefine perimeter end creation make feature -- Inicjalizacja make(center: POINT, s1, s2, angle: REAL) is -- Utwórz prostokąt o środku w punkcie center, o długościach s1 i s2 naprzeciwległych boków i orientacji angle do … end feature -- Dostęp side1, side2: REAL -- Długości naprzeciwległych boków diagonal: REAL -- Długość przekątnej perimeter: REAL is -- Długość obwodu, jako suma długości boków do Result := 2 * (side1 + side2) end …
132 Dziedziczenie Cel: potrzebna jest klasa prostokąt – cechy wspólne: np. przesuwanie, wyświetlanie, obracanie; - cechy charakterystyczne: np. przekątna, dokładnie cztery wierzchołki, kąty proste, łatwiejszy sposób liczenia obwodu invariant four_sides: count = 4 first_side: (vertices.i_th(1)).distance(vertices.i_th(2)) = side1 second_side: (vertices.i_th(2)).distance(vertices.i_th(3)) = side2 third_side: (vertices.i_th(3)).distance(vertices.i_th(4)) = side1 fourth_side: (vertices.i_th(4)).distance(vertices.i_th(1)) = side2 end 1 2 3 4 side1 side2
133 Dziedziczenie Klasa może być rozszerzeniem, specjalizacją lub kombinacją innych klas. Potomkiem klasy C jest dowolna klasa, która dziedziczy bezpośrednio lub pośrednio po C, łącznie z samą klasą C (formalnie C lub, rekurencyjnie, potomek potomka C). Właściwy potomek C to potomek tej klasy inny niż C. Przodkiem C jest taka klasa A, że C jest potomkiem A. Właściwy przodek C to taka klasa A, że C jest właściwym potomkiem A. Egzemplarz potomka może być postrzegany jako egzemplarz przodka, ale nie odwrotnie. Kod potomka zawsze wymienia przodka (inherit), ale nie na odwrót. Żadna klasa nie zna listy swoich potomków.
134 Dziedziczenie Klasa wielokąt POLYGON: –liczba wierzchołków –długość obwodu –wyświetl wielokąt na ekranie –obróć o kąt angle wokół środka center –przesuń w poziomie o a i w pionie o b –kolejne punkty określające wielokąt – lista powiązana –niezmiennik – wielokąt ma przynajmniej trzy wierzchołki
135 Dziedziczenie Klasa prostokąt RECTANGLE: –liczba wierzchołków – dokładnie 4 wierzchołki –długość obwodu – podwojona suma długości dwóch naprzeciwległych boków –wyświetl wielokąt na ekranie –obróć o kąt angle wokół środka center –przesuń w poziomie o a i w pionie o b –kolejne punkty określające prostokąt – lista powiązana –niezmiennik – wielokąt ma przynajmniej trzy wierzchołki –niezmiennik – prostokąt ma dokładnie cztery boki, a długości tych boków to side1, …, side4 –długość przekątnej –wszystkie kąty są proste
136 Dziedziczenie prostokąt wielokąt dziedziczy po obwód przekątna obwód niezmienniki
137 Dziedziczenie Reguła dziedziczenia niezmiennika – niezmiennik klasy jest logicznym iloczynem asercji obecnych w jej własnej klauzuli invariant oraz niezmienników przodków, jeśli takowe istnieją. Stwierdzenie, że B dziedziczy po A, oznacza jednocześnie, że B można postrzegać jako egzemplarz A. Wszystkie ograniczenia nałożone na egzemplarze A i wyrażone w niezmienniku, odnoszą się również do egzemplarzy B.
138 Dziedziczenie Reguła dziedziczenia procedur tworzących – status tworzenia w klasie przodka (czy jest procedurą tworzącą, czy nie) cechy odziedziczonej nie wpływa na jej status tworzenia w klasie potomnej. Rolą procedury tworzącej jest zapewnienie zgodności z niezmiennikiem. Wspólna dla wszystkich wielokątów procedura tworząca wygląda dziwacznie w kontekście prostokątów, ponieważ dla nich dopuszczalna jest tylko czteroelementowa lista spełniająca niezmiennik klasy RECTANGLE i odwrotnie procedura ta nie jest właściwa dla dowolnych wielokątów.
139 Polimorfizm Hierarchie dziedziczenia dają znaczną elastyczność w manipulowaniu obiektami, a przy tym pozwalają na korzystanie z bezpieczeństwa gwarantowanego przez statyczną kontrolę typów. Polimorfizm oznacza zdolność przyjmowania różnych postaci. W programowaniu obiektowym wiele postaci mogą przyjmować zmienne, encje lub elementy struktury danych, gdyż mają zdolność związywania się z obiektami różnych typów.
140 Polimorfizm w: wielokąt, p: prostokąt, t: trójkąt w := p w := t Tego rodzaju przypisania, w których typ źródła (prawa strona) jest inny niż typ celu (lewa strona), są nazywane przypisaniami polimorficznymi. Występująca w przypisaniu polimorficznym encja, taka jak w, jest nazywana encją polimorficzną. Typ źródła musi być zgodny z typem celu. Odwrotne przypisanie, np. p := w, nie jest poprawne.
141 Polimorfizm WIELOKĄT PROSTOKĄT w p w := p referencja
142 Polimorfizm Polimorfizm nie oznacza jakiejś transmutacji w trakcie wykonywania. Raz utworzony obiekt nie zmienia swojego typu. Zmianom podlegają jedynie referencje – mogą one być wielokrotnie wiązane z obiektami rożnych typów. Wiązania polimorficzne są dozwolone jedynie dla celów typu referencyjnego, a nie dla typu rozszerzonego. Jeśli jednak encja w byłaby typu rozszerzonego, to jej wartość byłaby bezpośrednio obiektem, a jakiekolwiek przypisanie do w zmieniłoby ten obiekt. Polimorfizm w takiej sytuacji nie jest możliwy.
143 Polimorfizm class ARRAY [G] creation … feature -- Zmina elementu put(v: G; i: INTEGER) is -- Przypisz v do elementu o indeksie i … end Rozważmy tablicę wielokątów: poly_arr: ARRAY [POLYGON] Wywołajmy cechę put: poly_arr.put(x, some_index) Typ x musi być zgodny z POLYGON. Nie musi to być dokładnie POLYGON, ponieważ każdy typ potomny jest również akceptowalny.
144 Polimorfizm w: wielokąt, p: prostokąt, k: kwadrat, t: trójkąt poly_arr.put(w, 1)poly_arr.put(p, 2) poly_arr.put(k, 3)poly_arr.put(t, 4) Takie struktury danych, zawierające obiekty różnych typów (z których wszystkie są potomkami jednego, wspólnego typu), są nazywane polimorficznymi strukturami danych. Również inne struktury kontenerowe, np. lista lub stos, mogą być polimorficzne.
145 Dziedziczenie i generyczność Za pomocą połączenia generyczności i dziedziczenia osiąga się maksymalną elastyczność i bezpieczeństwo. Połączenie pozwala na opisywanie struktur obiektowych, które są ogólne w takim stopniu, w jakim tego się oczekuje. LIST[prostokąt] LIST[wielokąt] LIST[figura] LIST[cokolwiek] Wybierając jako bieżący parametr generyczny klasę znajdującą się w odpowiednim miejscu w hierarchii, można nałożyć ograniczenia na wartości akceptowane przez pojemnik.