Paradygmaty programowania obiektowego a VB .NET (wftg 23IV2006)
Pradygmaty programowania obiektowego to podstawowe techniki projektowo-programowe, które pozwalają w pełni wykorzystywać zalety obiektowości w tworzeniu systemów informatycznych. Techniki te nadal podlegają ewolucji. Wspierane są przez techniki projektowe UML.
Paradygmaty tworzenia klas:
Abstrakcja
Kreacja
Hermetyzacja (zasłanianie implementacji)
Dziedziczenie (UML: Uogólnienie)
Polimorfizm
Interfejs (Interface, jako klasa abstrakcyjna)
Kompozycja (zawieranie)
Ad 1. Abstrakcja
Tworząc wzorzec obiektu, czyli klasę, opieramy się na cechach modelowanych obiektów, tych które nas interesują. To projektant musi abstrahować od cech wg niego nieistotnych dla całości systemu - na tym polega proces abstrakcji - sensowny wybór z niezliczonej ilości cech i zachowań obiektu świata rzeczywistego i wyrażenie tego poprzez typy danych i procedury języka programowania. Zwykle proces abstrakcji dotyczy klas modelowanej przez system dziedziny - tzw. klas użytkownika, w przeciwieństwie do klas narzędziowych, które technicznie wspierają proces projektowania (przykład: klasa Pracownik kontra klasa Graphics).
Ad 2. Kreacja
Często tworząc zestaw klas narzędziowych (choć nie tylko) dochodzimy do wniosku, że potrzebna jest specyficzna hierarchia klas obiektów. Takie obiekty (klasy), które dobrze służą jako środki techniczne, ale nie posiadają odpowiedników w świecie naturalnym. Dla podkreślenia specyfiki tego postępowania wprowadziłem określenie KREACJA, choć można się upierać iż to przejaw abstrakcji.
Ad 3. Hermetyzacja
Projektując klasę definiujemy zachowanie obiektu, sposób kontaktu z tym obiektem oraz jego wewnętrzną strukturę. Użytkownik obiektu (czyli inny obiekt wykorzystujący go lub współpracujący z nim) nie musi znać struktury wewnętrznej obiektu projektowanego. Musi za to mieć zagwarantowane jak z nim się kontaktować i czego może oczekiwać w wyniku takiej komunikacji. Nazywamy to interfejsem obiektu, czyli sposobem kontaktu z obiektem. Jest to pojęciowo szersze znaczenie niż definiowane w punkcie 6. Hermetyzacją (niektórzy nazywają to np. kapsułkowaniem) jest ukrycie przed użytkownikiem struktury wewnętrznej. Są ku temu co najmniej dwa powody: Nie znając struktury wewnętrznej użytkownik nie może ingerować w szczegóły techniczne obiektu. A więc m. in. nie może go uszkodzić dokonując sprytnych acz nieprzemyślanych modyfikacji (robiąc zmiany narusza przy tym kontrakt - klasa będzie się zachowywała trochę inaczej a to może zmylić projektantów polegających na pierwotnej specyfikacji. Pamiętajmy, że projekt to zespołowa praca i indywidualistyczne podejście jest niezgodne z regułami). Ważniejszym powodem jest możliwość zmiany struktury obiektu przez jego projektanta - po to by ulepszyć obiekt (efektywność), czy zmodyfikować strukturę współdziałających z nim, wewnętrznie, klas. Sposób wewnętrznej konstrukcji obiektu nazywamy IMPLEMENTACJĄ klasy.
Język Visual Basic .NET do celów hermetyzacji używa modyfikatorów dla klas, pól i metod takich jak Public, Protected, Private, Friend, Protected Friend oraz właściwości, czyli konstrukcji Property: wirtualnych pól tworzonych przez odpowiednią parę funkcja-procedura, wraz z modyfikatorami ReadOnly, WriteOnly i ReadWrite.
Ad 4. Dziedziczenie (UML: Uogólnienie)
Języki programowania obiektowego pozwalają tworzyć hierarchię klas, co daje strukturę drzewiastą: klasa ma jednego rodzica, choć ten może mieć wielu potomków. Jest to jednobazowy schemat dziedziczenia, w przeciwieństwie do nielicznych języków wielobazowego dziedziczenia, jakie np. oferuje język C++. Dziedziczenie oznacza, że potomek składa się z tych samych składowych co rodzic a prócz tego dokłada swoją specyficzną funkcjonalność. Jest więc uszczegółowieniem, specjalizacją w stosunku do rodzica. Rodzic natomiast jest uogólnieniem, generalizacja swoich potomków. Zamiast rodzic-potomek(dziecko) mówimy też o nadklasie i klasie, klasie i podklasie itp. co łatwo zrozumieć z kontekstu. Dziedziczenie jest nazwą wziętą z technik programowania. W UML ten rodzaj zależności nazywa się Uogólnieniem. Są tu dwa aspekty sprawy ujęte pod hasłem dziedziczenie.
Po pierwsze na dziedziczenie możemy spojrzeć z perspektywy wielokrotnego wykorzystania modułu. Traktujemy wtedy klasę jako daną nam (gotową) bibliotekę/moduł i korzystamy z niej/niego. Warunkiem użycia jest utworzenie własnej podklasy i następnie jej obiektów.
Po drugie możemy dziedziczenie traktować jako szansę na jednolite traktowanie dzieci różnych typów, czyli jako uogólnienie. Ta cecha wraz z polimorfizmem daje nam potężne narzędzie tworzenia jednolitej pętli przebiegającej po zbiorowości złożonej z obiektów z różnych podklas, bez używania instrukcji If testujących podtyp(klasę) obiektu . Jest to jedno z bazowych zastosowań polimorfizmu i dziedziczenia.
Ad 5. Polimorfizm
(nie mylić z przeciążaniem funkcji: identyczna nazwa w różnych podklasach kontra identyczne nazwy w klasie ale z różnymi sygnaturami))
W podklasach tego samego poziomu możemy przedefiniowywać metodę przodka (rodzica) zachowując jej nazwę. Wtedy mamy różne zachowania się podklas obiektów, zgodne z ich specyfiką. Wraz z użyciem uogólnienia mamy efekt opisany wyżej.
Przykład dot. punktu 4 i 5 - ad. uogólnienie
Class Towar
...
Protected Ilość As Integer ---- Ilość opakowań danego towaru w magazynie
Public MustOverride Function Cena As Decimal (zamiast Function lepiej Property )
…
End Class
------------------------------------------------
Class Sypkie
Inherits Towar
…
Public Overrides Function Cena As Decimal
…
End Function
End Class
-------------------------------------------------
Class Sztuki
Inherits Towar
…
Public Overrides Function Cena As Decimal
…
End Function
End Class
-------------------------------------------------
Class ZMetra
Inherits Towar
…
Public Overrides Function Cena As Decimal
…
End Function
End Class
--------------------------------------------------
Załóżmy, że ceny jednostkowe tych podtowarów wyrażane są w różnych jednostkach, więc i funkcja cena różni się implementacją, gdyż towar Sypkie sprzedawany jest w opakowaniach po litr, towar Sztuki w opakowaniach po 100 a ZMetra w 10 metrowych. Stąd wzory na obliczenie ceny są różne.
Pętla obliczająca wartość towarów w magazynie, gdzie cena to funkcja polimorficzna
...
Dim tab(MAX) As Towar ------------------------- oto ujednolicenie podtypów
Dim wartość as Decimal=0
…
For I=0 To MAX
wartość+=tab(I).Ilość*tab(I).Cena -------------każde i może być innego podtypu a wzór
jest jednolity !!!
Next
...
Ad 6. Interface
Jest to rodzaj kontraktu; narzucenie pewnej klasie innego typu niż jej podstawowy, bo przecież klasa jest pewnym typem. Obiekt gra w różnych zastosowaniach w systemie różne role, a to w programowaniu oznacza, że zachowuje się jak obiekt innego typu. Ale jednobazowe dziedziczenie narzuca mu bycie obiektem jednego typu. Oto przykład sklepu ze zwierzętami. Załóżmy, że klasą podstawową jest ZWIERZĘ. Ale inną klasą jest TOWAR i to zwierzę z punktu widzenia systemu księgowego jest towarem. Zwierzęta niektóre karmi się innymi zwierzętami, a więc istnieje klasa POKARM. Konkretne zwierzę jest egzemplarzem klasy ZWIERZĘ ale może też być egzemplarzem POKARM-u i jest na pewno egzemplarzem TOWAR-u. Tu widać że wielobazowe dziedziczenie jest konieczne a język nie dostarcza takich środków. Dlatego stosuje się w tym celu Interface - klasę abstrakcyjną. Klasą do której należy obiekt zwierzę jest klasa ZWIERZĘ, czyli typem jest ZWIERZĘ. Jednak gdy klasa ZWIERZĘ będzie implementowała Interface TOWAR oraz Interface POKARM, to obiekt będzie i typu ZWIERZĘ i typu POKARM i typu TOWAR, czyli może grać w systemie wiele różnych ról koniecznych w różnych podsystemach informatycznych sklepu.
Interfejs jest klasą abstrakcyjną. Określa jakie zachowanie jest wymagane by implementujący go obiekt (a ściślej - jego klasa) mógł być uznany za obiekt typu zdefiniowanego przez tenże interfejs. Stąd na początku padło określenie, że interfejs to rodzaj kontraktu (nie mylić z szerokim pojęciem interfejsu jako wszystkich sposobów kontaktu z obiektem). Interface deklaruje publiczne metody i właściwości a klasa która chce być jego podtypem definiuje te metody i właściwości przez frazę Implements.
Czyli w przykładzie:
Interface Towar
…
Function Cena() As Decimal ------ deklaracja
…
End Interface
Interface Pokarm
…
Function Kaloryczność100G() as Double
…
End Interface
Class Zwierzę
Implements Towar, Pokarm ------ kontrakty: “CHCĘ BYĆ TYPU Towar I POKARM”
…
Public Function Cena() As Decimal Implements TOWAR.Cena()
… ------- definicja wynikła z kontraktu
End Function
…
End Class
Okazuje się że w ten sposób Interface pozwala na uogólnienie jak dziedziczenie.
Gdyby stworzyć podklasy zwierząt, to można by obliczyć wartość zwierząt w sklepie jako towaru, a z punktu widzenia pokarmu, choć to brzmi okrutnie, te same zwierzęta można podsumować jako żywność (tu: kaloryczność całkowita pokarmu).
Powiedziano, że Interface to klasa abstrakcyjna. Nie można tworzyć jej obiektów, bo obiekty typu Interface żerują na konkretnej klasie. Natomiast wspomniane wyżej sumowanie można przeprowadzić, bo wolno utworzyć tablicę typu interfejsowego!!!
Dim tab(MAX) As Towar
ponieważ jej elementami mogą być obiekty tych konkretnych klas (jak np. Zwierzę), które implementują interfejs Towar!
Nie omówiłem tu wszystkich kontekstów stosowania interfejsu. Jednym z ważnych jest tworzenie klas usługodawców, gdzie w ramach wymagań ze strony usługodawcy definiuje się interface, jaki muszą spełniać potencjalni (nowe klasy) usługobiorcy. Opisałem to w innym miejscu.
Ad 7. Kompozycja
Kompozycja jest techniką, która jest nośna z projektanckiego punktu widzenia, natomiast nie wymaga specyficznych syntaktycznych środków technicznych. Po prostu zamiast dziedziczenia z nadklasy w celu wielokrotnego użytku możemy w klasie zawrzeć obiekt tej (niedoszłej nad)klasy.
A WIĘC ZAMIAST WYKORZYSTAĆ PRZEZ DZIEDZICZENIE JAK PONIŻEJ:
Class A
…
Public Sub BardzoSkomlikowanaProcedura()
...
End Sub
…
End Class
Class B
Inherits A
…
End Class
I UŻYWAMY:
…
Dim ob As New B
ob.BardzoSkomlikowanaProcedura
…
MOŻEMY WYKORZYSTAĆ KOD KLASY PRZEZ KOMPOZYCJĘ:
Class B
Private kapsułka As New A
…
Public Sub BardzoSkomlikowanaProcedura() --- ta sama nazwa dla użytkownika
kapsułka.BardzoSkomlikowanaProcedura() --- a zleca wykonanie obiektowi klasy A
End Sub
...
End Class
I UŻYWAMY TAK SAMO !!!:
…
Dim ob As New B
ob.BardzoSkomlikowanaProcedura
…
Po przykładach I omówieniu widać, że techniki dziedziczenia i polimorfizmu można zastąpić interfejsami i kompozycją. Lepiej, bo możemy symulować wielobazowe dziedziczenie. Gorzej, bo nie można zasymulować wielokrotnego dziedziczenia
Strona 6 z 6
Strona 1 z 6