Podsumowanie obiektowości w programowaniu (VB .NET) 10IV2005, wftg
Podstawą obiektowości jest abstrakcja. Należy przez to rozumieć, iż projektowana klasa / obiekt(instancja klasy) zastępuje fragment rzeczywistości prezentując swoją podstawową funkcjonalność, tj. tylko istotne dla modelowanego problemu cechy i zachowanie. Obiekt jawi się przez swój interfejs - publiczne możliwości manipulowania nim. Implementacja (sposób realizacji; definicje metod, pól itp.) na tym etapie jest ukryta (stanowi czarną skrzynkę): albo nie jest jeszcze zrealizowana albo celowo maskowana, by nie zakłócać obrazu obiektu w ramach struktury i funkcjonowania całości projektu (UML, PSI - perspektywa pojęciowa klasy - w odróżnieniu od perspektywy specyfikacyjnej i perspektywy implementacyjnej.)
Projektowanie obiektowe systemu to tworzenie obiektowej struktury systemu - konstrukcja współistnienia obiektów jako kontenerów, zbiorowości, podległości (hierarchii) a z drugiej strony funkcjonowania: wzajemnej komunikacji i zaprojektowanie sposobów tej komunikacji. Możliwe jest to dzięki obiektowemu paradygmatowi - pojęciu obiektu (i jego definicji: klasy), który jest tworzony dynamicznie (który można też interpretować jako dynamiczną przestrzeń nazw), który zawiera w sobie zarówno dane jak i metody. To podejście zmienia całkowicie metodykę projektowania jak i myślenia o składowych programu/systemu.
Trzy podstawowe cechy programowania obiektowego to:
możliwość wielokrotnego wykorzystania kodu klas/bibliotek, nazwijmy to współużywalność klas (reusing)
jednorodne traktowanie - polimorfizm - funkcjonalności obiektów różnych klas z określonego punktu widzenia (względem określonego interfejsu)
Pierwsza z wymienionych cech ułatwia modułową budowę i wielokrotne wykorzystanie już stworzonych podsystemów (klas w postaci pakietów) - usprawnia to projektowanie: przyspiesza, ułatwia i zwiększa niezawodność.
Druga cecha pozwala podczas programowania na zmniejszenie ilości sytuacji wyjątkowych, symetryzacji problemu obsługi obiektów, ułatwia m.in. na przyszłe rozszerzanie/rozbudowę systemu o nowe podklasy w taki sposób, by nie naruszało to działania (kodu) istniejących podsystemów - wystarczy dodać nową podklasę (a zawiera ona przecież swoją funkcjonalność w metodach).
Ta właściwość wpisuje się w podstawowe w obiektowości dążenie do wzajemnej izolacji w definicji klas - im mniejsza (wyrażona w definicji pól i kodzie metod) wzajemna współzależność, tym bardziej klarowna struktura, łatwiejsza podmienialność modułów (grup klas, pakietów), większa więc niezawodność oprogramowania. To dążenie idzie w przeciwnym kierunku niż budowa całego systemu, która polega na komplikacji: zarówno ilości obiektów jak i wzajemnej relacji. Ale właśnie dzięki hermetyzacji (izolacji) obiektu ułatwione jest panowanie nad całym systemem.
Do uzyskania wspomnianych w p. 3. pożądanych cech przyczyniają się techniki i paradygmaty obiektowe:
Ad 3a ) dziedziczenie klas oraz/lub kompozycja klas (w zakresie struktury systemu)
Ad 3b) abstrakcyjny interfejs (Interface w sensie VB .NET) wraz z polimorfizmem (w zakresie funkcjonalności systemu)
Ad 3c) Abstrakcja wspomniana w p.1 jest wspomagana przez technikę hermetyzacji klasy.
Jeśli chodzi o brak wielo-bazowego dziedziczenia - p. 4a (VB .NET, Java, C#) to można je zastąpić kompozycją klas (kompozycja to zawarcie w nowo tworzonym obiekcie prywatnych obiektów współ-używanych klas - klas które mogłyby grać rolę nadklas dla tego obiektu, gdyby było wielo-dziedziczenie i udostępnienie ich metod (wzorzec projektowy: Fasada)).
Jeśli chodzi o funkcjonalność - p. 4b - to polimorfizm jest naturalnym sposobem jednolitego traktowania zbiorowości obiektów różnych podklas dziedziczących z danej nadklasy. A jeśli jej nie można (kolejnej, równorzędnej nadklasy - jedno-bazowe dziedziczenie!) utworzyć, to temu samemu celowi może posłużyć implementowanie interfejsu przez te różne (pod)klasy - w ten sposób (tablica referencji - o typie danego interfejsu!) obiekty różnych klas stają się elementami wspólnej zbiorowości i podlegają polimorfizmowi z punktu widzenia danego interfejsu (Interface'u).
Potrzeba jednolitego traktowania klas (tu: jako usługobiorców) pojawia się także gdy istnieje klasa uługodawca. Taka klasa może mieć wymagania co do zachowania się usługobiorcy - czyli żądania co do metod, które na pewno, prócz typowych dla swej klasy, powinien spełniać (czytaj: implementować) usługobiorca. Umożliwia to komunikację od obiektu usługodawcy do usługobiorcy. Jednak usługodawca zwykle zaprojektowany jest uniwersalnie i może obsłużyć usługobiorców, których klasy jeszcze nie były zdefiniowany w momencie jego projektowania. I wtedy projektant klasy-usługodawcy tworzy dodatkowo Interface z zestawem metod, które wg niego mają dostarczyć potrzebną funkcjonalność, deklarując metody koniecznie pożądane w usługobiorcy. Projektant zaś nowej klasy, która prócz swej podstawowej funkcjonalności ma „przy okazji” być usługobiorcą, musi zaimplementować ten Interface; zgodzi się w ten sposób na kontrakt, warunki narzucone przez usługodawcę. Oczywiście projektant usługodawcy tworzy interfejs i żąda jego spełnienia, by osiągnąć pewną funkcjonalność, więc prócz technicznej deklaracji kontraktu jako Interface'u daje też dodatkowy semantyczny opis: określa, czego oczekuje do implementacji metod tego interfejsu, jaka funkcjonalność jest potrzebna. Bez tego opisu projektant klasy usługobiorcy ma za mało informacji - deklaracja wymaganego interfejsu to za mało, by wiedzieć co mają robić metody!
W punkcie 2. zwrócono uwagę na strukturę i funkcjonalność. Ten drugi problem polega m.in. na wzajemnej komunikacji obiektów. Spojrzenie obiektowe na system zmienia sposób widzenia zagadnienia wywoływania funkcji/procedury/dostępu do zmiennej publicznej. Przejawia się to już w nazewnictwie, gdy mówimy o metodach zamiast o funkcjach/procedurach. Wywołanie metody to komunikacja między obiektami. Obiekt wysyłający komunikat to obiekt (nadawca komunikatu), w którego funkcji/procedurze wywołana jest metoda (wysłanie komunikatu) docelowego obiektu. Ten docelowy obiekt jest odbiorcą komunikatu. Wykonanie metody tego wywołania w obiekcie docelowym to reakcja odbiorcy na komunikat. Ze względu na brak hermetyzacji powinno się unikać używania pól publicznych. Ich użycie jest też formą komunikacji (zarówno odczyt jak i zapis), niezalecane gdyż nie podlegające kontroli (w jaką mogą i powinny być wyposażone metody) - Visual Basic .NET zaleca więc dla tego celu Property Get/Set - wtedy też jawnie widać iż taki dostęp do danych jest komunikacją równoważną metodom.
Jeśli metoda jest funkcją, to zwrócona (do wywołującego tę funkcję obiektu nadawcy) w wyniku wywołania wartość jest odpowiedzią, komunikatem zwrotnym, który tym razem w wyniku reakcji wysłał obiekt docelowy do obiektu nadawcy.
W przypadku wywołania funkcji mamy przykład komunikacji synchronicznej lub pseudosynchronicznej, a w przypadku procedury komunikacja jest synchroniczna lub asynchroniczna. To nazewnictwo dotyczy jedno- lub wielo-wątkowości w programie. Z natury rzeczy funkcja wysyła parametry i czeka na odpowiedź, a więc ten rodzaj komunikacji polega na zsynchronizowaniu akcji “pytanie-odpowiedź ”. Obiekt nadający czeka (nic nie robiąc) aż dostanie odpowiedź, bo funkcja w wyniku wykonania musi zwrócić wartość. Natomiast procedura to zwykle kontakt jednokierunkowy i w takiej sytuacji nadawca nie musi czekać na reakcję odbiorcy (czyli na zakończenie wykonania wywołanej procedury). Jeśli program jest jednowątkowy, to nadawca czeka bo musi - taka jest technika wywołania, wszystko po kolei: wywołanie, wykonanie, powrót. Natomiast gdy kontaktujące się obiekty są w różnych wątkach (albo same są wątkami), to wywołanie może sprowadzić się do zainicjowania metody wywoływanej w odbiorcy i kontynuacji swoich instrukcji. Wtedy ta wywołana w odbiorcy metoda - reakcja - toczy się współbieżnie, a nawet, gdy wątki obsługiwane są przez różne procesory, toczy się realnie jednocześnie! Zależnie od problemu asynchroniczność jest albo efektem ubocznym wielowątkowości albo zjawiskiem celowo wykorzystywanym w konstrukcji oprogramowania - to drugie ma zwykle miejce w systemach tzw. czasu rzeczywistego, czyli w sterowaniu przez komputer środowiska zewnętrznego, w automatyce. Ponieważ obecne programowanie okienkowe opiera się na zdarzeniach i procedurach zdarzeniowych, to technika wielowątkowości stosowana jest także w programowaniu systemów przetwarzania. Środowisko wykonawcze .NET implementuje mechanizmy wykonywania wielowątkowego programu i VB .NET też można stosować te techniki. Wielowątkowość może być implementowana albo jako cecha środowiska wykonawczego albo/i jako właściwość systemu operacyjnego - w tym drugim przypadku jest to bardziej uniwersalny mechanizm (co nie znaczy, że środowisko nie może symulować tej cechy po swojemu - ignorując system operacyjny).
Sam fakt wywołania funkcji/procedury (bez parametrów) już jest wysłaniem komunikatu (binarnego, czyli: zaszło/nie zaszło). Czasami jest to wystarczająca informacja, którą komunikat ma przenieść. Jeśli przekazane informacje (komunikat) mają być bardziej złożone, to przekazujemy je przez parametry aktualne wywołania.
Pamiętać też trzeba, że gdy przekazujemy parametr typu ByRef, najczęściej obiekt, to powodem jest albo chęć przekazania adresu zamiast dużej porcji danych (efektywność wywołania) - czyli aspekt czysto techniczny albo celem jest przekazanie referencji do obiektu, którego właściwość może zmienić odbiorca, zyskując w ten sposób kanał komunikacji zwrotnej: bo po wywołaniu wywołujący sprawdzi tę właściwość w obiekcie który przekazał jako parametr typu ByRef i zauważy zmienioną wartość. Ten sposób stosujemy w funkcji, gdy uważamy że jej wynik to niewystarczający środek komunikacji zwrotnej albo w procedurze i wtedy można uznać że zachowuje się ona jak funkcja - dostarcza bowiem wynik do obiektu wywołującego!