Personal tools

PO Wprowadzenie do programowania obiektowego

From Studia Informatyczne

<<< Powrót do przedmiotu Programowanie obiektowe

Spis treści

Wprowadzenie

Na pierwszym wykładzie omówiliśmy, czym są i jak odnajdywać klasy pojęciowe. Przypomnijmy, że rozpoznanie klas pojęciowych pozwala lepiej zrozumieć dziedzinę problemu. Na tym wykładzie pokażemy, że myślenie obiektowe ułatwia tworzenie programów i wyjaśnimy, co wyróżnia obiektowe języki programowania. Pokażemy również, że odnajdywanie klas pojęciowych nie było jedynie ćwiczeniem. Będzie się można na nich wzorować podczas projektowania i tworzenia programu, jednocześnie zmniejszając lukę reprezentacji.

Obiekty a programowanie

Rozpoczęliśmy tworzenie modelu obiektowego i dzięki temu lepiej rozumiemy dziedzinę. Oczywiście nasz model nie jest od razu kompletny, ale warto się na nim wzorować podczas projektowania programu do gry w Monopol i tym samym ograniczyć lukę reprezentacji. Patrząc na diagramy ze stanem poszczególnych obiektów nie trudno o porównanie klas do rekordów, a atrybutów do pól rekordów. To dobre skojarzenia. W językach obiektowych klasy rzeczywiście pełnią rolę typów danych, a obiekty są wartościami tych typów. Prawdziwą rewolucję podejście obiektowe wprowadza jednak dopiero w sposobie organizacji kodu operującego na tych danych. Jest on również zebrany w klasach. Czyli dane i kod na nich operujący są razem! Kod w każdej klasie jest podzielony na metody, które wywołuje się na rzecz jakiegoś egzemplarza. Metody mogą mieć parametry i dawać jakieś wartości. Ponadto mają bezpośredni dostęp do danych egzemplarza, na rzecz którego są wykonywane, oraz mogą te dane modyfikować. Na poniższym diagramie widać klasę Kostka wraz z jej metodami wymienionymi w trzeciej przegródce. Metoda losujWartość() nie ma żadnych parametrów i nie przekazuje wyniku. Z jej nazwy można się domyślać, że losuje nową wartość atrybutu wskazywanaWartość. Metoda getWartość() : Integer również jest bezparametrowa, ale typem jej wyniku jest klasa Integer. Z jej nazwy można się domyślać, że daje wartość, która w chwili jej wywołania jest przypisana na atrybut wskazywanaWartość. Warto zwrócić uwagę, że dla tego atrybutu również określono, że przechowuje obiekt klasy Integer. Trzy kropki umieszczone w trzeciej przegródce po ostatniej metodzie oznaczają, że klasa posiada jeszcze inne metody, ale ich nie pokazano.

Klasa programowa

Atrybuty i metody jednej klasy nazywa się jej składowymi (ang. class member).

Mimo, że na powyższym diagramie użyliśmy tej samej notacji, nie jest to już model dziedziny. Zaczęliśmy projektować oprogramowanie. Wiemy mniej więcej, jakie informacje będą przechowywane w poszczególnych klasach. Trzeba określić typy ich atrybutów, zdecydować jak zrealizować powiązania oraz rozdzielić metody.

Od klasy pojęciowej do klasy projektowej

Po podjęciu tych decyzji i pokazaniu ich na diagramie nie mamy już do czynienia z klasami pojęciowymi, które przedstawiają rzeczywistość, ale z klasami projektowymi (ang. design class), które przedstawiają projekt oprogramowania. Można powiedzieć, że programowanie obiektowe polega na wyznaczaniu obiektom odpowiedzialności. Te odpowiedzialności są dwóch typów: za pamiętanie pewnych danych i za wykonywanie operacji na tych danych. Przy czym zdecydowanie trudniejsze i ważniejsze jest rozdzielenie odpowiedzialności za wykonanie operacji. Wszystkie szczegóły, w jaki sposób obiekty wywiązują się z tych odpowiedzialności, są ukryte w definicji klasy. Pozwala to łatwiej zapanować nad złożonością. Pisząc metody danej klasy, możemy się skupić tylko na wycinku całego zadania – na realizacji odpowiedzialności wyznaczonych tej klasie. Jeżeli do wywiązania się z tych odpowiedzialności potrzeba wykonać operacje na danych innych obiektów, po prostu się im te operacje zleca. To tak samo, jak w dobrze zorganizowanej firmie, która zatrudnia ekspertów z różnych dziedzin. Każdy obiekt odpowiada za swój kawałek i nie ingeruje w pracę pozostałych.

Co z powiązaniami?

Ponieważ klasy projektowe mają być zaimplementowane, nie wystarczy wiedza, że występują między nimi powiązania. Trzeba zdecydować, jak te powiązania zrealizować. Zazwyczaj jedna z klas uczestniczących w powiązaniu będzie posiadała atrybut, na który zostanie przypisany obiekt drugiej klasy. Taki atrybut nazywa się referencją (ang. reference). Na poniższym diagramie widać dwa równoważne sposoby pokazania, że klasa Gracz posiada referencję o nazwie pionek, która wskazuje obiekt klasy Pionek.

Sposoby przedstawiania referencji

W pierwszym wypadku referencja jest pokazana jako powiązanie. Atrybutu będącego referencją nie wymienia się w drugiej przegródce z pozostałymi atrybutami. Jego nazwę umieszcza się nad linią reprezentującą powiązanie, po stronie klasy, której egzamplarz będzie wskazywany przez referencję. Na tym samym końcu powiązania znajduje się strzałka wskazująca widoczność. W tym wypadku to obiekty klasy Gracz mają referencję do obiektów klasy Pionek, a nie na odwrót. Referencje pokazuje się jako powiązanie, jeżeli obie klasy są warte uwagi. Jeżeli definicja klasy, do której prowadzi referencja, jest oczywista bądź nieistotna z punktu widzenia tego, co chcemy pokazać na tym diagramie, to referencję można pokazać jako zwykły atrybut i wskazać jego typ.

Zwróćmy uwagę, że również nazwa oraz stanKonta są referencjami. Klasy String i Integer są standardowo używane do przechowywania odpowiednio napisów oraz liczb całkowitych. Gotowe implementacje tych lub bardzo podobnie nazywających się klas wraz z wieloma przydatnymi metodami są dostępne w praktycznie wszystkich obiektowych językach programowania.

Pamiętaj, że sposób realizacji powiązania jest decyzją projektową. Nie należy podejmować decyzji projektowych już podczas tworzenia modelu dziedziny.

Typy podstawowe

W większości obiektowych języków programowania są również nieobiektowe typy danych. Nazywa się je typami podstawowymi lub prostymi (ang. primitive type lub basic type). Typy podstawowe służą do przechowywania pojedynczych znaków, liczb całkowitych i rzeczywistych różnej precyzji oraz wartości logicznych. Są dodawane do języków obiektowych z dwóch powodów. Po pierwsze, ich wartości naturalnie nadają się do używania w wyrażeniach, a wypadku obiektów trzeba się posługiwać metodami. Po drugie, posługiwanie się nimi jest w wielu sytuacjach bardziej efektywne.

Hierarchie klas

Czasami zdarza się, że potrzebujemy kilku wariantów pewnej klasy. Ze wszystkich wariantów będziemy korzystać w taki sam sposób, ale chcemy żeby różniły się pod pewnymi względami. Implementując grę w Monopol trzeba np. zdefiniować klasy odpowiadające różnym rodzajom pól. Wszystkie rodzaje pól będą mieć nazwę i opis oraz będą posiadały metodę wykonującą czynności związane z odwiedzeniem pola przez któregoś z graczy. Treść tej metody zależy od rodzaju odwiedzanego pola i będzie inna dla pola, które można nabyć, pola szansy czy pola poboru opłaty podatkowej. Poza nazwą, opisem i tą metodą poszczególne rodzaje pól mogą zawierać inne składowe. Większość obiektowych języków pozwala w tym celu zdefiniować hierarchię klas. Klasa bazowa (ang. base class) bądź inaczej nadklasa (ang. superclass) definiuje składowe wspólne dla wszystkich wariantów. Klasy pochodne (ang. derived class) bądź inaczej podklasy (ang. subclass) definiują pozostałe składowe, które występują tylko w poszczególnych wariantach. Związek między nadklasą i podklasami nazywany jest uogólnieniem (ang. generalization), bądź związkiem uogólnienie-uszczegółowienie. Na diagramach uogólnienie pokazywane jest przy pomocy ciągłej linii łączącej klasę bazową i klasę pochodną, która od strony klasy bazowej zakończona jest niewypełnioną trójkątną strzałką.

Dziedziczenie

Podstawowym kryterium pozwalającym określić kiedy stosować uogólnienie jest odpowiedź na pytanie:

 Czy podklasa jest szczególnym przypadkiem nadklasy?

Jeżeli tak, to znaczy, że obiekty podklasy są również egzemplarzami nadklasy i powinny dawać się używać w tych samych kontekstach.

Dziedziczenie

Aby nie powielać kodu, klasa pochodna dziedziczy (ang. inherits) wszystkie składowe klasy bazowej. Jeżeli zachowanie niektórych metod powinno być zmienione, klasa pochodna może je przedefiniować (ang. override) podając nowe definicje. Ponieważ klasa pochodna może również zawierać nowe składowe, często mówi się również, że podklasa rozszerza (ang. extend) nadklasę.

Hierarchia klas dla gry w Monopol

Na poniższym diagramie pokazano część hierarchii pól z gry w Monopol. Aby łatwiej było zorientować się, które metody są przedefiniowane ich deklaracje powtarza się w podklasie, jeżeli przedefiniowanie rzeczywiście zachodzi. W przeciwnym przypadku metoda jest dziedziczona w niezmienionej postaci z nadklasy i podklasa nie powtarza jej deklaracji.

Hierarchia Pól

Kontrakty i widoczność

Czas na kolejną analogię do organizacji świata rzeczywistego. Dobrze zorganizowana firma zatrudnia specjalistów z różnych dziedzin, którzy współpracują razem i nawzajem się uzupełniają. Każdy z nich ma wyznaczony zakres obowiązków. Pracownicy nie ingerują w to, jak pozostali wykonują swoje obowiązki, bo od razu zrobiłby się bałagan. Podobnie, jeżeli kilka firm współpracuje przy realizacji dużego kontraktu, zadania każdej z nich są ściśle określone. Każda odpowiada za inny aspekt prac. Ich wysiłki nawzajem się uzupełniają, jednak żadna nie ingeruje w to, jak pozostałe organizują swoją pracę. Ich wzajemne zależności określa realizowany kontrakt. Ścisłe określenie w kontrakcie czego firmy mogą od siebie oczekiwać pozwala im dowolnie reorganizować i usprawniać sposób pracy, póki efekt jest zgodny z kontraktem. W razie konieczności możliwe jest również zastąpienie jednej z firm przez inną.

Wyznaczanie odpowiedzialności obiektom można porównać do ustalania zakresu obowiązków pracownikom lub współpracującym firmom. Do wywiązania się ze swoich odpowiedzialności obiekty będą zazwyczaj potrzebować dodatkowych atrybutów i metod. Takie pomocnicze składowe powinny być ukryte przed obiektami innych klas. Dzięki temu inne klasy nie zaczną na nich polegać i zmiany spowodowane wprowadzaniem usprawnień w implementacji będą ograniczone tylko do jednej klasy. Żeby rozdzielić szczegóły kontraktu od szczegółów implementacji w językach obiektowych występuje pojęcie widoczności (ang. visibility). Składowe klasy mogą być oznaczone jako:

  • publiczne (ang. public) – mogą ich bez ograniczeń używać obiekty wszystkich klas,
  • chronione (ang. protected) – mogą ich bez ograniczeń używać obiekty tej samej klasy lub jej podklas[1] oraz
  • prywatne (ang. private) – mogą ich używać jedynie obiekty tej samej klasy.

W niektórych językach obiektowych występują dodatkowe poziomy widoczności, np. w Javie jest jeszcze poziom dla klas definiowanych w tym samym pakiecie.

Na diagramach z klasami projektowymi widoczność wskazuje się odpowiednio przy pomocy znaków +, # oraz -, które umieszcza się przed nazwą składowej. Na poniższym diagramie pokazano widoczność składowych klasy PoleWłasność.

Widoczność składowych klasy PoleWłasność

Kapsułkowanie

Przed rozpowszechnieniem programowania obiektowego, wszystkie dane były globalnie dostępne i można było na nich operować w dowolny sposób i z każdego miejsca w kodzie. To często prowadziło do problemów oraz błędów. Niestaranny programista mógł niewłaściwie obchodzić się z danymi lub wręcz pozostawiać je w niespójnym stanie. Mogło to powodować trudne do zdiagnozowania błędy w zupełnie innej części programu. Możliwość zebrania danych i operacji na tych danych razem w definicjach klasy ułatwia unikanie takich błędów. Jednak pełne bezpieczeństwo daje dopiero ukrywanie atrybutów. Jeżeli inne obiekty chcą poznać lub zmienić ich wartości, muszą w tym celu skorzystać ze specjalnych metod. Takie ukrywanie swoich danych przez obiekty nazywa się kapsułkowaniem. Stosowanie kapsułkowania to bardzo dobry nawyk i warto go u siebie wyrobić. Można zezwolić na wykonywanie na danych operacji tylko określonego rodzaju (np. tylko odczytywania ich wartości) oraz związać z wykonywanymi operacjami jakieś czynności dodatkowe. Co więcej w razie konieczności wykonania jakichś zmian w implementacji, np. struktury danych, ich zasięg będzie ograniczony.

Zwróćmy uwagę, że atrybuty cena i czyOddaneWZastaw w klasie PoleWłasność są prywatne. Do odczytywania i zmieniania ich wartości służą odpowiednio metody dajCenę() oraz oddajWZastaw(), wykupZastaw() i czyOddaneWZastaw(). Dzięki takiemu rozwiązaniu obiekty innych klas nie mogą zmieniać ceny pola, a jedynie ją odczytywać (cena prawdopodobnie jest ustawiana w jakiejś niepokazanej tu metodzie inicjującej). Dla pola czyOddaneWZastaw istnieją metody zarówno przekazujące jego wartość, jak i ją zmieniające. Jednak w tym przypadku przy każdej zmianie wartości tego pola wykonywane są dodatkowe czynności, jak zmiana stanu konta jego właściciela. Uczynienie pola prywatnym wymusza używanie tych metod i gwarantuje, że czynności dodatkowe nie zostaną pominięte.

Konwencja get, set i is

Z kapsułkowaniem wiąże się pewna konwencja. Jeżeli atrybut ma nazwę jakaśNazwa to metoda, która odczytuje jego wartość powinna się nazywać getJakaśNazwa() a metoda, która pozwala tą wartość zmienić setJakaśNazwa(). W wypadku atrybutów przechowujących wartości logiczne metoda odczytująca wartość jest też czasami nazywana isJakaśNazwa().

Nie istnieją ogólnie przyjęte polskie odpowiedniki tych przedrostków. Dobrym pomysłem wydaje się stosowanie oryginalnych przedrostków z atrybutami nazywanymi po polsku. Dodatkowym powodem, żeby tak robić jest fakt, że wiele bibliotek oraz narzędzi wspomagających tworzenie kodu zakłada stosowanie tej konwencji.

Jak wyznaczać odpowiedzialności?

Najtrudniejsze w programowaniu obiektowym jest wyznaczenie odpowiedzialności klasom, tak aby wspólnie realizowały postawione zadanie. Dla każdego problemu istnieje wiele istotnie różnych rozwiązań, z których każde ma swoje silne i słabe strony. Wyjaśnimy tu jak się za to zabrać oraz na co zwracać uwagę porównując konkurencyjne pomysły.

Diagramy przebiegu

Na projektowych diagramach klas planowaliśmy, z jakich odpowiedzialności klasy będą musiały się wywiązać. Można to nazwać spisywaniem kontraktu. Decyzję co zawrzeć w kontrakcie podejmuje się podczas planowania, jak obiekty ze sobą mają współpracować w działającym systemie. Używana przez nas do tej pory notacja (diagramy klas) nie nadaje się do przedstawiania interakcji obiektów. Służą do tego diagramy przebiegu. Na poniższym przykładzie pokazano, że obiekt GraWMonopol zleca odpowiedzialność za obsługę postoju na polu, które może mieć właściciela do obiektu reprezentującego to pole. Jeżeli pole nie zostało jeszcze przez nikogo zakupione, gracz decyduje, czy chce je nabyć i jeżeli tak, to obiekt PoleWłasność zleca sam sobie odpowiedzialność za obsłużenie zakupu. W efekcie rachunek odwiedzającego gracza jest obciążany ceną pola oraz obiekt PoleWłasność zapamiętuje odwiedzającego gracza jako swojego właściciela.

Diagram przebiegu

Oczywiście na diagramie nie pokazano szczegółów wszystkich operacji, np. w jaki sposób gracz decyduje, czy chce nabyć pole i sprawdza, czy go na to stać. To najważniejsza zaleta notacji graficznej. Bardzo łatwo można pokazać interesujące nas aspekty działania systemu, a zbędne w danej chwili szczegóły pominąć.

Zastanowienie się, jak obiekty mają ze sobą współpracować, jest bardzo ważne i bez tego nie da się prawidłowo zaplanować ich składowych. Dlatego pracę należy rozpoczynać od tworzenia diagramów przebiegu, a diagramy klas rozbudowywać w międzyczasie przyrostowo. Na tym wykładzie pokazaliśmy najpierw diagramy klas tylko dlatego, że musieliśmy wytłumaczyć podstawowe zagadnienia programowania obiektowego.

Luźne sprzężenie

Sprzężenie (ang. coupling) jest miarą jak bardzo obiekty, podsystemy lub systemy zależą od siebie nawzajem. Przykładowo obiekt wykonujący metodę innego obiektu jest z nim sprzężony. Tak samo podklasa jest sprzężona z nadklasą. Sprzężenie obiektów utrudnia wprowadzanie zmian w kodzie. Im luźniej obiekty są ze sobą sprzężone, tym zasięg potencjalnych problemów wywołanych zmianą jednego z nich jest mniejszy. Jeżeli w trakcie wyznaczania odpowiedzialności chcesz wybrać jedno z kilku rozwiązań, zdecyduj się na to, dla którego poziom sprzężenia obiektów jest najmniejszy.

Wyjątkiem jest tutaj sprzężenie z bibliotekami lub systemami, w których praktycznie nie występują zmiany. Przykładowo nie jest groźne wysokie sprzężenie ze standardowymi bibliotekami z używanego przez nas języka programowania. Takie biblioteki praktycznie się nie zmieniają. Jest bardzo mało prawdopodobne, że sprzężenie z nimi sprawi problemy.

Wysoka spójność

Spójność (ang. cohesion) to miara jak funkcjonalnie powiązane są metody danej klasy. Warto dbać o utrzymanie wysokiej spójności (ang. high cohesion). Jeżeli jedna klasa odpowiada za zbyt wiele zagadnień (jest niespójna), z czasem nadmiernie się rozrośnie i będzie trudna do zrozumienia oraz pielęgnacji. Nie należy bać się tworzenia klas pomocniczych, którym będzie zlecana część odpowiedzialności.

Wysoka spójność

Początkowo może się wydawać, że zachowanie wysokiej spójności przez zlecanie odpowiedzialności ma negatywny wpływ na sprzężenie. Jednak klasy mające wiele odpowiedzialności są zazwyczaj sprzężone z wieloma innymi klasami. Jest przez to dużo bardziej prawdopodobne, że będą dotykane przez zmiany. A zmiany w dużej i niespójnej klasie wprowadza się dużo trudniej. Można popełnić wiele błędów, przed którymi programowanie obiektowe miało chronić.

Ponownie narzuca się tu porównanie do organizacji pracy ludzi. Czasami początkujący szefowie nie mogą oprzeć się pokusie i chcą zbyt wiele kontrolować, zamiast zlecić pracę swoim podwładnym. Szybko przekonują się, że od pewnego momentu wszystkiego kontrolować się nie da i takie zachowanie kończy się katastrofą. Tak samo jest z programowaniem. Początkujący programiści zbyt dużo chcą zrealizować w jednej klasie, tym samym utrudniają sobie zadanie, zamiast podzielić problem i zlecić odpowiedzialności. Pamiętaj, że dzielenie kodu na spójne klasy jest esencją obiektowości.

Wzorce projektowe

Przy wyznaczaniu odpowiedzialności obiektom istnieje wiele poziomów swobody. Każde rozwiązanie ma zazwyczaj swoje silne i słabe strony. Niektóre są łatwiejsze w pielęgnacji, a inne bardziej elastyczne. Z biegiem czasu każdy programista rozbudowuje stosowany przez siebie repertuar pomysłów i rozwiązań. Z doświadczenia zna również ich wady i zalety. Takie sprawdzone pomysły o dobrze poznanych silnych i słabych stronach nazywa się wzorcami projektowymi (ang. design pattern). Wzorce projektowe posiadają nazwy jak Fabryka (ang. Factory), Singleton (ang. Singleton), Strategia (ang. Strategy) i Fasada (ang. Facade). Dobrze dobrane i ogólnie przyjęte nazwy wzorców ułatwiają ich zapamiętywanie oraz usprawniają komunikację międzyludzką (np. w zespole programistycznym dyskutującym jak rozwiązać nowy problem).

Fakt

Wzorzec projektowy to nazwana i dobrze znana para problem/rozwiązanie wraz z zaleceniami, jak zastosować rozwiązanie w nowym kontekście, oraz analizą jego silnych i słabych stron, sposobów implementacji, odmian, itd.

Posługiwanie się wzorcami projektowymi nie jest domeną jedynie doświadczonych programistów. Wzorców można i trzeba się uczyć. Istnieje bardzo wiele publikacji i materiałów na ich temat. Na programowaniu obiektowym, przy okazji omawiania poszczególnych tematów, zostaną omówione najważniejsze związane z nimi wzorce projektowe.

UML

Notacja graficzna, której dotychczas używaliśmy podczas wykonywania OOA/D to Unified Modeling Language (UML). Jest to graficzny język opracowany specjalnie w celu specyfikowania i planowania artefaktów programistycznych oraz usprawnienia komunikacji między członkami zespołu programistycznego. Prace nad UML zapoczątkowali "trzej amigos" - Grady Booch, Jim Rambough i Ivar Jacobson. UML jest powszechnie stosowany, a od 1997 roku jest standardem Object Management Group (OMG) i doczekał się już drugiej wersji. Największą zaletą UML jest to, iż jest to notacja graficzna. Rozpoznawanie symboli jest silną stroną ludzkiego mózgu, dlatego oglądając prostokąty i linie na diagramach łatwo zrozumieć zależności między obiektami. Co więcej, tak jak każda notacja graficzna, UML pozwala skupić uwagę na najważniejszych koncepcjach lub pomysłach i pominąć bądź ukryć nieistotne szczegóły.

Wyliczając zalety nie wolno zapomnieć, że UML to tylko notacja. Sama znajomość notacji i ewentualnie opanowanie jakiegoś narzędzia CASE (Computer Aided Software Engineering) nie wystarczy żeby zostać analitykiem, projektantem lub programistą. To tak samo jak znajomość języka polskiego i umiejętność posługiwania się edytorem tekstu nie czyni od razu pisarzem.

Rodzaje diagramów

UML jest bardzo rozbudowany, zawiera kilkanaście rodzajów diagramów. Każdy rodzaj diagramu UML może mieć wiele zastosowań. Notacja UML zostanie szczegółowo omówiona na zajęciach z Inżynierii oprogramowania. Na programowaniu obiektowym poznałeś już diagramy klas (ang. class diagram). To ta notacja była użyta podczas modelowania dziedziny oraz podczas projektowania klas, z których będzie składał się program. Diagramy klas, na których modelujemy dziedzinę, potocznie nazywa się dziedzinowymi diagramami klas (ang. domain class diagram). Diagramy klas używane do projektowania klas, z których będzie składał się program, potocznie nazywamy się projektowymi diagramami klas (ang. design class diagram).

Do pokazywania, jak obiekty współpracują, aby wspólnie zrealizować zadanie, służą diagramy interakcji (ang. interaction diagram). W UML są dwa rodzaje diagramów interakcji: diagramy przebiegu (ang. sequence diagram) oraz diagramy komunikacji (ang. communication diagram). Oba rodzaje diagramów interakcji dają takie same możliwości. Diagramy przebiegu mają bogatszą notację i łatwiej widoczna jest na nich kolejność komunikatów. Diagramy komunikacji pozwalają natomiast swobodnie rozmieszczać obiekty, przez co lepiej się sprawdzają, gdy trzeba pokazać wiele obiektów, a ilość miejsca jest ograniczona. Na programowaniu obiektowym będziemy używać jedynie diagramów przebiegu.

Program do rysowania

Do rysowania dziedzinowych diagramów klas, projektowych diagramów klas oraz diagramów przebiegu możesz użyć prostego i darmowego programu Violet. Żeby z niego skorzystać, ściągnij go najpierw na swój komputer (możesz to zrobić klikając na odnośniku prawym klawiszem myszy i wybierając "Zapisz jako...").

Violet można również osadzać na stronach HTML jako Aplet. Niestety ze względów bezpieczeństwa wersja działająca w ten sposób ma wyłączoną część funkcji, w tym zapisywanie efektów pracy do pliku. Jeżeli posiadasz zainstalowane środowisko uruchomieniowe Java, Violet powinien wyświetlać się poniżej.


Rysowanie na kartce i na tablicy

Mimo, że diagramy używane w notatkach z programowania obiektowego były przygotowane na komputerze, najłatwiej rysuje się je na kartce lub na tablicy. Jeżeli chciałbyś utrwalić efekty takiej pracy lub udostępnić je elektronicznie, zrób po prostu zdjęcie aparatem cyfrowym.

Wybór języka programowania

Od klasy projektowej do klasy programowej

Prawdopodobnie najpopularniejszym językiem programowania używanym w tej chwili jest Java. Dlatego właśnie ten język będzie przez nas użyty podczas nauki programowania obiektowego. Składnia Javy jest wzorowana na składni języków C/C++. W przeciwieństwie jednak do C++, gdzie cechy obiektowe są uzupełnieniem, Java jest obiektowa od podszewki. Prace nad Javą rozpoczęły się w 1991 roku w firmie Sun Microsystems. Początkowo język nazywał się Oak i miał być używany do tworzenia programów wbudowanych w różnego rodzaju elektroniczne urządzenia domowe jak magnetowidy czy systemy antywłamaniowe. Jednak Sun nie dogadał się z producentami tych urządzeń, a ponieważ programy pisane w nowym języku bez żadnych zmian dawało się uruchamiać na różnych systemach operacyjnych i architekturach sprzętowych, w 1995 roku postanowiono zmienić nazwę języka na Java i przystosowano go do tworzenie aplikacji WWW. Okazało się to strzałem w dziesiątkę. WWW właśnie stawało się bardzo popularne, a Java zawiera wiele przydatnych bibliotek, które ułatwiały programowanie w Internecie. Co więcej w Javie można było pisać Aplety, czyli sieciowe programy z graficznym interfejsem użytkownika, które daje się osadzić na stronach HTML prawie tak samo łatwo jak obrazki. Przez pewien okres Aplety były bardzo popularne i w ten sposób tworzono m.in. gry sieciowe, czaty, proste systemy wykonujące obliczenia i wizualizacje naukowe czy systemy analizy giełdowej i finansowej.

Java szybko zyskała popularność. Już w 1997 poświęcona temu językowi konferencja JavaOne stała się najliczniejszą konferencją programistyczną na świecie. Język był cały czas rozwijany, a liczba dostępnych bibliotek nieustannie się rozrastała. Na Javie oparto też wiele technologii i specyfikacji. Z czasem głównym zastosowaniem języka stało się tworzenie przenośnych, skalowalnych, wielowarstwowych aplikacji biznesowych. W zastosowaniach multimedialnych Aplety zostały wyparte przez inne technologie, jak Adobe Flash i JavaScript.

Najważniejsze wyróżniające cechy Javy, dzięki którym zawdzięcza swoją popularność, to:

  1. obiektowość – programy są dzielone na moduły nazywane klasami; każda klasa łączy dane oraz operacje, które można na tych danych wykonywać; taki sposób organizacji kodu ułatwia tworzenie dużych systemów oraz współpracę wielu programistów,
  2. niezależność od platformy – programy w Javie są uruchamiane w maszynie wirtualnej (ang. Java Virtual Machine), której wersje istnieją dla różnych systemów operacyjnych i architektur, dzięki temu raz napisany program działa tak samo, niezależnie od tego, gdzie jest uruchamiany,
  3. prostota – restrykcyjny kompilator chroni przed popełnianiem niektórych błędów (np. używania niezainicjowanych zmiennych); wielu innych po prostu nie da się popełnić, gdyż szczegóły realizacji podstawowych mechanizmów języka są niedostępne dla programistów; nie można na przykład samemu manipulować wskaźnikami w pamięci, czy stosować trików związanych reprezentacją typu logicznego; co więcej w maszynę wirtualną wbudowany jest automatyczny odśmiecacz, który zwalnia programistę z konieczności zwalniania nieużywanej pamięci,
  4. sieciowość – Java zawiera wiele bibliotek ułatwiających tworzenie kodu działającego w sieci oraz obsługę najważniejszych protokołów i standardów,
  5. bezpieczeństwo – język zawiera mechanizmy umożliwiające podpisywanie kodu oraz ograniczające wykonywanie potencjalnie niebezpiecznych operacji; przykładowo, skoro Aplety są ściągane z odwiedzanych stron WWW, działają w tak zwanej piaskownicy (ang. sandbox) i nie mogą wykonywać niektórych operacji, jak czytanie oraz pisanie po dysku twardym.

Przykładowe Aplety

Wiele ciekawych przykładowych Apletów wraz z ich kodem źródłowym znajdziesz na stronach firmy Sun. Najciekawsze to:

Również Violet – prosty i darmowy program do rysowania diagramów może działać jako Aplet.

Przypisy

  1. W Javie jest jeszcze domyślny dostęp pakietowy, a składowe chronione są również dostępne dla wszystkich klas z tego samego pakietu. Dodatkowo w podklasach z innego pakietu składowe chronione są dostępne jedynie przez dziedziczenie, ale nie przez dostęp referencyjny.