1 / 24

Typy w językach i systemach obiektowych (i nie tylko) Cześć 2.

Typy w językach i systemach obiektowych (i nie tylko) Cześć 2. Kazimierz Subieta Instytut Podstaw Informatyki PAN, Warszawa subieta@ipipan.waw.pl http://www.ipipan.waw.pl/~subieta Polsko-Japońska Wyższa Szkoła Technik Komputerowych, Warszawa. Zmienność. mutability.

hieu
Download Presentation

Typy w językach i systemach obiektowych (i nie tylko) Cześć 2.

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Typy w językach i systemach obiektowych(i nie tylko)Cześć 2. Kazimierz Subieta Instytut Podstaw Informatyki PAN, Warszawa subieta@ipipan.waw.pl http://www.ipipan.waw.pl/~subieta Polsko-Japońska Wyższa Szkoła Technik Komputerowych, Warszawa

  2. Zmienność mutability Zmienność jest dodatkową własnością, która może być przypisana do bytu programistycznego (zmiennej, obiektu, parametru) lub, poprzez reguły wnioskowania o poprawności typologicznej, do wyrażenia. Byt posiadający tę własność może podlegać istotnej operacji, mianowicie aktualizacji. Tej operacji nie można wykonywać na bycie nie posiadającej tej własności. Np. wartość 5 nie posiada własności zmienności; operacja (2+3) := 6 powinna zostać odrzucona ze względów typologicznych. Dla zmiennej intX operacja X := 6 jest legalna. Wartość 5 nie ma przypisanej własności zmienności, zaś zmiennej X przypisuje się taką własność. Potrzeba przypisywania własności zmienności do bytów programistycznych jest konsekwencją używania w tym samym kontekście syntaktycznym tego samego typu zarówno dla wartości jak i zmiennej przechowującej taką wartość.

  3. Semantyka cechy zmienności Większość języków programowania dokonuje przypisania atrybutu zmienności poprzez reguły syntaktyczne rozróżnienie deklaracji stałych i deklaracji zmiennych. Ten sposób zastosowano również w standardzie ODMG gdzie projektant ma możliwość rozróżnienia pomiędzy obiektami, tj. bytami z przypisanym atrybutem zmienności, oraz literałami (literals), tj. bytami których nie można zmieniać; poza własnością zmienności cechy obiektów i literałów koncepcyjnie niewiele się różnią. Własność zmienności przypisuje się do parametrów procedur. W językach z rodziny Pascala każdy parametr formalny z atrybutem zmienności jest poprzedzany słowem kluczowym var. W standardzie ODMG i wielu innych językach takim słowem jest out. Ta deklaracja oznacza, że odpowiednie wyrażenie będące parametrem aktualnym tej procedury ma wartościować się do bytu programistycznego, który posiada własność zmienności. Oznacza ona również, że wewnątrz ciała procedury aktualizacje tego parametru nie dotyczą parametru jako takiego, lecz tego bytu programu (zwykle zmiennej, obiektu, atrybutu) na który taki parametr wskazuje. Technikę tę określa się jako call-by-reference. Z reguły przypisanie atrybutu zmienności do pewnego bytu w tekście programu oznacza, że wynikiem jego wiązania będzie referencja.

  4. Przykład zastosowania zmienności voidzmień(outintMojaZmienna) begin intX; czytaj( X ); //Wczytanie wartości X z klawiatury MojaZmienna = X; end; Taka procedura może być wywołana w następującym kontekście; typedef TypPrac = struct { stringNazwisko, intRok_urodz, intZarobek } .... TypPrac Pracownik; Pracownik = (Nazwisko: „Nowak”, Rok_urodz: 1955, Zarobek: 3000); zmień( Pracownik . Zarobek ); Istotne w tym programie jest to, że chcemy w niej zmienić zarobek Nowaka, wczytując go z klawiatury. Deklaracja outintMojaZmienna oznacza, że jako parametru oczekujemy refererencji do zewnętrznego bytu programistycznego, który posiada własność zmienności. Ta referencja uzyskuje wewnątrz procedury lokalną nazwę MojaZmienna. ! Błąd typu zmień( Pracownik.Zarobek + 1000 );

  5. Inne techniki określania zmienności Niektóre instrukcje języka programowania dokonują przypisania zmienności implicite. Np. instrukcja wyrażenie1 := wyrażenie2; ustala, że wyrażenie1 ma zostać zwartościowane do wartości zmienialnej. Taką wartość określa się jako L-value, od słowa Left oznaczającego, że dotyczy to lewej strony podstawienia. wyrażenie2 po ewaluacji zwraca wartość, która nie musi mieć własności zmienialności. Taką wartość określa się jako R-value, od słowa Right oznaczającego, że dotyczy to prawej strony podstawienia. Podobnie instrukcja usuwania obiektu o przykładowej postaci deletewyrażenie; oczekuje, że wyrażenie zostanie zwartościowane do L-value; w przeciwnym przypadku sygnalizowany będzie błąd typu.

  6. Programowanie ogólne generic Programowaniem ogólnym nazywa się technikę tworzenie programów operujących nie na konkretnej strukturze danych, ale na szerokiej rodzinie takich struktur. Przykładami programów ogólnych są: program sortujący dowolną listę, program wyświetlający zawartość dowolnej relacyjnej bazy danych, program tłumaczący dowolny program w Pascalu na program w C, kompilator, SZBD, itp. Programowanie ogólne jest jedną z własności sprzyjających ponownemu użyciu (reuse). Jest ono nieodzownym składnikiem programowania systemowego. Polimorfizm typów jest niezbędną cechą takiego języka programowania, który ma mieć jednocześnie możliwość programowania ogólnego oraz pełną statyczną kontrolę typów. Niestety, jak wspomnieliśmy, polimorfizm typów wprowadza złożoność, która jest trudno akceptowalna dla programistów.

  7. Inne techniki programowania ogólnego Z reguły, uniemożliwiają lub ograniczają mocną kontrolę typów. Operatory zmiany typu (koercje) zmieniające struktury złożone na struktury prymitywne; np. cast w językach C i C++ umożliwia potraktowanie zapisu jak sekwencji bajtów. Niekiedy wiąże się to z przejściem na niższy poziom językowy, np. programowanie w assemblerze. Funkcje wyższego rzędu, tj. funkcje, których parametrami są funkcje; przykładem udostępniania takiej techniki jest język LISP, ML, SCHEME oraz Smalltalk. Parametryzowane klasy, klasy szablonowe (templates); Refleksja (reflection), czyli dynamiczna generacja kodu programu. Programowanie z refleksją oznacza, że programista pisze program, który wygeneruje kawałek kodu; ten kod jest automatycznie uruchamiany z programu, który go utworzył. Technika ta wymaga późnego wiązania, jest również trudna dla programistów. Jej elementy można odnaleźć w dynamicznym SQL. Istnieją techniki, które pozwalają wprowadzić do mechanizmu refleksji statyczną kontrolę typu, ale zgodnie z naszą najlepszą wiedzą, nie wyszły one dotąd poza stadium teoretycznych rozważań.

  8. Co to jest zgodność typów? Niech wyr będzie pewnym wyrażeniem języka programowania, które jest użyte jako aktualny parametr wywołania procedury proc(wyr). Procedura proc została zadeklarowana przez programistę jako voidproc( Tpar ) { ... }; gdzie T jest typem parametru formalnego par. Podczas analizy składniowej poprzez zastosowanie reguł wnioskowania o typie, wyrażenie wyr jest odrzucone jako typologicznie niepoprawne, lub ma przypisany pewien typ, powiedzmy T1. Zarówno T jak i T1 są pewnymi wyrażeniami językowymi, które wiążą nazwy typów atomowych (int, real, char, boolean, ...), konstruktory typu (array, record, set, sequence,...), nazwy typów skonstruowanych przez programistę, nazwy pól w strukturach oraz inne elementy, w tym składniowe. Powstaje pytanie, jaka powinna być zależność pomiędzy typem T i typem T1, aby wywołanie procedury proc(wyr) można było zakwalifikować jako typologicznie poprawne. Odpowiedź na to pytanie wyznacza zasadę zgodności typów.

  9. Nazwowa zgodność typów (1) Klasyczne języki programowania, np. Pascal, przyjmują w takiej sytuacji tzw. zgodność nazwową. Oznacza ona, że typ T1 jest określony nazwą typu atomowego lub typu skonstruowanego przez programistę, zaś typ T musi być dokładnie tą samą nazwą. Jeżeli np. programista zdefiniował nowy typ, np. numer, który jest identyczny z typem integer, następnie zaś użył typu numer w deklaracji parametru procedury, to nie może następnie wywołać tej procedury z wyrażeniem typy integer, a wyłącznie z parametrem typu numer: type numer = integer; TypZapisu = record X: numer, Yinteger end; ... procedureproc( numerpar ) ... ; ... var R: TypZapisu; ... proc( R.X ); (* Wywołanie typologicznie poprawne *) proc( R.Y ); (* Wywołanie typologicznie niepoprawne *)

  10. Nazwowa zgodność typów (2) Nazwowa zgodność typów jest dość łatwa w implementacji i posiada zalety dla modelowania pojęciowego. Umożliwia ona konstruowanie typów o różnej intencji pojęciowej. Jak widać z powyższego przykładu, numer jest reprezentowany jako integer, ale jest pojęciowo innym typem. W szczególności (przyjmując ortodoksyjny punkt widzenia) nie wolno do niego stosować np. operacji +, gdyż nie została ona dla tego typu zdefiniowana. Niestety, zgodność nazwowa okazała się też nieco niewygodna w wielu sytuacjach (głównie ograniczenie możliwości programowania generycznego). W związku z tym niektóre języki (np. Modula-2) stosuje się zgodność nazwową o dość liberalnych regułach; w szczególności, typy nazwane przejmują zestaw operacji z typu będącego podstawą ich definicji. Nazwowa zgodność typów znalazła zastosowanie w nowym standardzie SQL3 w postaci tzw. typów rozróżnionych (distinct types).

  11. Strukturalna zgodność typów Strukturalna zgodność typów przyjmuje, że nazwa typu jest tylko zastępczym środkiem reprezentującym pewną strukturę i może być przez tę strukturę zastąpiona. Takie zastępowanie nazw wprowadzonych przez projektanta lub programistę przez odpowiadające im struktury prowadzi się aż do pełnego wyeliminowania tych nazw z wyrażenia językowego ustalającego typ. Po takim zabiegu pozostają więc tylko nazwy typów atomowych, nazwy konstruktorów typów, nazwy pól w strukturach i pewien lukier syntaktyczny. Metoda ta oznacza bardziej liberalną kontrolę typu. Przykładowo, przy zastosowaniu tej metody oba podane wyżej wywołania procedury proc są poprawne. Uważa się, że strukturalna zgodność typu bardziej pasuje do obiektowości (patrz omówioną wyżej własność zastępowania), jest koncepcyjnie prostsza i bardziej uniwersalna. Mimo niekonsekwencji w zakresie modelowania pojęciowego, jest wystarczająco skuteczna.

  12. Wady strukturalnej zgodności typów W niektórych sytuacjach strukturalna zgodność typów oraz splątanie pojęcia typu z pojęciem klasy prowadzą do paradoksów, które mają pewne odbicie w obiektowej literaturze. Przykładowo załóżmy, że klasa Prostokąt jest zdefiniowana jako typ typedefProstokąt = struct{intX, intY} gdzie X i Y oznaczają jego szerokość i wysokość, zaś klasa Kwadrat jest zdefiniowana jako typ typedefKwadrat = struct{intX} gdzie X oznacza zarówno szerokość jak i wysokość. Nasze doświadczenie podpowiada, że kwadrat jest podklasą prostokąta. W tym przypadku okazuje się, że Prostokąt jest podklasą klasy Kwadrat, ponieważ ma więcej atrybutów! Tego rodzaju paradoksy są skutkiem pewnej aproksymacji koncepcji typów, podtypów, klas i dziedziczenia w stosunku do modelowania pojęciowego. Podany przykład (jeden z wielu) świadczy o tym, że pojęcia obiektowości są jednak pewnym uproszczeniem w stosunku do sytuacji zdarzających się w modelowaniu pojęciowym. Prawdopodobnie każda formalna dyscyplina musi prowadzić do pewnych paradoksów pomiędzy potocznym odczuciem a własnościami formalnymi. Wyeliminowanie podanego wyżej paradoksu jest dość łatwe po rozpoznaniu jego istotnej przyczyny, ale powoduje skomplikowanie modelu obiektowego.

  13. Rozszerzalność systemu typów Cecha rozszerzalności systemu typów jest standardowa dla bardzo wielu języków, w szczególności, dla całej rodziny języków Pascalo-podobnych (Pascal, Modula, Ada, itp.). Polega ona na tym, że istnieje pewien zestaw typów atomowych (integer, real, char, boolean, itd.) oraz pewien zestaw konstruktorów typów (array, record, set_of, sequence_of, itd). Programista ma możliwość definiowania typu poprzez zastosowanie typów atomowych oraz konstruktorów typów, zgodnie z pewnymi zasadami syntaktycznymi, ale w gruncie rzeczy bez większych ograniczeń w zakresie kombinowania i zagnieżdżania konstruktorów typów. Co więcej, tak skonstruowany typ może nazwać i następnie posługiwać się tą nazwą w dalszych definicjach tak samo jak nazwą typu atomowego.

  14. DBPL: przykład rozszerzania typów TYPE TypDostawcy = RECORD nrdostawcy: ARRAY[0..2] OF CHAR; nazwisko: ARRAY[0..19] OF CHAR; status: INTEGER; miasto: ARRAY[0..19] OF CHAR; END; TypRelacjiDostawcy = RELATION nrdostawcy OF TypDostawcy; Powyżej znajdują się nazwane definicje dwóch typów, TypDostawcy i TypRelacjiDostawcy. RECORD, ARRAY i RELATION są konstruktorami typów; CHAR i INTEGER są typami atomowymi. Mając tak zdefiniowane typy można zdefiniować dowolną liczbę zmiennych posiadających takie typy, np. VAR MojDostawca: TypDostawcy; (* Zmienna dla przechowywania jednej krotki *) Dostawcy: TypRelacjiDostawcy; (* Deklaracja relacji Dostawcy *) ZwolnieniDostawcy: TypRelacjiDostawcy; (* Inna relacja tego samego typu *)

  15. Rozszerzalność typów a model relacyjny W popularnych relacyjnych systemach zarządzania bazami danych nie ma możliwości konstruowania i nazywania typów oraz ich wykorzystania do deklaracji struktur danych. Cecha rozszerzalności systemu typów jest obca dla ideologii relacyjnej, co spowodowało powstanie takiego tematu (mimo, że wydawało się, że przestał on istnieć od czasu Algolu). Systemy relacyjne ograniczają system typów do jednego, z góry zdefiniowanego konstruktora typu (relacji) i nie dopuszczają ortogonalnej kombinacji tego i innych konstruktorów. Co więcej, systemy te wiążą w jednym zdaniu deklarację typu z deklaracją zmiennej (relacji) tego typu. Te ograniczenia są konsekwencją rozwoju historycznego, ale także doktrynalnie przyjętych, ideologicznych ograniczeń modelu danych (podyktowanych dążeniem do prostoty lub rzekomej „zgodności” z przyjętym aparatem matematycznym). Obiektowość słusznie zrywa z tego rodzaju ideologiczno-paranoicznymi ograniczeniami, postulując pełną ortogonalność i rozszerzalność systemu typów.

  16. Cechy rozszerzalnego systemu typów System typów zawiera pewną liczbę typów atomowych, takich jak integer, real, char, itd. System typów zawiera pewną liczbę konstruktorów typów (których elementami są wartości lub obiekty - w zależności od systemu), takich jak record, struct, array, oraz konstruktorów typów masowych, takich jak zbiór (set), wielozbiór (bag), sekwencja (sequence). System typów daje możliwość deklarowania wariantów (lub unii, w terminologii C i C++); System typów daje możliwość konstruowania nowych typów poprzez dowolne, ortogonalne zastosowanie konstruktorów typów do typów atomowych lub typów już skonstruowanych; Nie występują różnice w użyciu przez programistę typu atomowego i typu skonstruowanego; System typów daje możliwość konstruowania sygnatur (specyfikacji parametrów wejściowych i wyniku) dla funkcji, procedur, operacji i metod. System typów daje możliwość nazwania dowolnego typu atomowego, skonstruowanego, itd. Zdefiniowaną nazwę typu można używać do konstruowania nowych typów lub sygnatur. Deklaracja typu jest niezależna od deklaracji struktury danych (np. obiektu) tego typu. Dla jednego typu można zdefiniować dowolną liczbę struktur danych posiadających ten typ.

  17. Co to znaczy “mocna kontrola typu”? Mocna kontrola typu oznacza, że wszystkie byty programistyczne (obiekty, zmienne, procedury, funkcje, metody, operacje, moduły, klasy, ADT) podlegają obowiązkowej specyfikacji typu, zaś każda operacja w programie (w szczególności, wyrażenia arytmetyczne i boolowskie, porównania, operatory podstawienia, wywołania procedur) jest sprawdzana na zgodność z tą specyfikacją. Ta kontrola odbywa się podczas czasu kompilacji programu (jest to określane jako kontrola statyczna); wyjątkowo, w nielicznych sytuacjach ta kontrola jest oddelegowana do czasu wykonania programu (jest to określane jako kontrola dynamiczna). Przykładami kontroli dynamicznej jest kontrola zakresu indeksów tablic lub kontrola poprawności użycia wariantów zapisów. Istotna kontrola typu powinna być kontrolą statyczną. Kontrola dynamiczna jest bardziej kosztowna, gdyż stanowi obciążenie czasu wykonania. Ponadto, jest znacznie mniej skuteczna, gdyż błąd typu może pozostać nie wykryty aż do momentu kiedy dany kawałek kodu zostanie wykonany, i to przy odpowiednim stanie. Z punktu widzenia własności niezawodnościowych systemów ten błąd typologiczny niczym nie różni się od innych błędów wykonania.

  18. Wady mocnej kontroli typów Mocna kontrola typu ma aspekty negatywne. Przede wszystkim, powoduje znaczne zmniejszenie mocy języka i jego elastyczności. Klasycznym przykładem z języka Pascal jest uniemożliwienie napisania w tym języku procedur operujących na tablicach o dowolnych rozmiarach; np. procedury mnożącej dwie dowolne macierze. Tego rodzaju problemy muszą być rozwiązywane przy pomocy doraźnych programistycznych trików, np. poprzez programowanie wykorzystujące wskaźniki, które nie podlegają kontroli typów, nie są uniwersalne, oraz zaciemniają konstrukcję programu. Własności takie jak: późne wiązanie, wartości zerowe (null values), warianty, perspektywy, procedury bazy danych, dynamiczne klasy, ewolucja schematu, etc. wymagają dynamicznej kontroli typu (zwykle, mało skutecznej) lub żmudnego ponownego kompilowania (konsolidowania) wszystkich napisanych programów po każdej dynamicznej zmianie środowiska w związku z wymienionymi wyżej cechami (np. po wprowadzeniu lub usunięciu perspektywy), co w wielu przypadkach jest nie do przyjęcia.

  19. Elementy mocnej kontroli typów (1) Specyfikacja typów wszystkich zmiennych i obiektów, które występują w programie, włącznie z określeniem ich powiązań z innymi obiektami oraz metod, np. typedefTypPrac = struct{ stringnazwisko, intzarobek, Działpracuje_w, intzarobek_netto( ) }; TypPrac Pracownik; Specyfikacja sygnatur wszystkich operatorów, procedur, funkcji, metod, np. booleansprawdź( inTypPracprac, inTypDziałdzial, outintile_lat_pracuje ) Specyfikacja interfejsów modułów, klas i innych hermetyzowanych abstrakcji programistycznych. Dla parametrów procedur, funkcji, metod: określenie które z nich są wejściowe (call-by-value), a które wyjściowe (call-by-reference). W zależności od tego, operacje na tych parametrach (np. podstawienie) mogą być dozwolone lub niedozwolone.

  20. Elementy mocnej kontroli typów (2) Określenie reguł wnioskowania o typie dla wszystkich konstrukcji składniowych języka programowania, szczególnie dla wyrażeń. Reguła wnioskowania o typie jest przypisana do danej produkcji gramatyki bezkontekstowej języka. Przyjmuje ona pewne założenia co do typów związanych z symbolami nieterminalnymi występującymi po prawej stronie takiej produkcji, następnie ustala regułę jaki będzie typ związany z symbolem nieterminalnym występującym po lewej stronie tej produkcji. Dzięki temu, w procesie analizy gramatycznej programu można wywnioskować jaki będzie wynikowy typ każdej konstrukcji, która w tym programie występuje. Przykładowo, jeżeli typ X jest integer (zmienialny), zaś typ Y jest real (zmienialny), to dla wyrażenia X + Y reguła wnioskowania o typie wyprowadzi typ real (niezmienialny). W procesie analizy i wnioskowania o poprawności typologicznej ustalone są między innymi typy rzeczywistych parametrów aktualnych poszczególnych wywołań procedur, metod, etc. Te typy są porównywane z typami parametrów formalnych wyspecyfikowanymi przez programistę podczas deklaracji danej procedury lub metody. Niezgodności są sygnalizowane jako błędy typu.

  21. Przykład reguły wnioskowania o typie • Niech produkcja gramatyczna ma postać: • predykat ::= wyrażenie1 = wyrażenie2 • zaś T(symbol_nieterminalny) oznacza typ przypisany do danego symbolu nieterminalnego. Reguła wnioskowania o typie może mieć następującą postać: T(wyrażenie1)  {integer, real, char, boolean} and (T(wyrażenie1) = T(wyrażenie2) or (T(wyrażenie1)  {integer, real} and T(wyrażenie2)  {integer, real} )) T(predykat) = boolean poprzednik następnik Jeżeli w danym kontekście warunek poprzednika nie jest spełniony, to sygnalizowany jest błąd typu. Całości wyrażenia przypisywany jest typ podany w następniku. Podana reguła jest bardzo uproszczona. Dochodzą do tego meta-reguły takie jak zasada zastępowania, kowariancja, itd. Typowa technika implementacyjna nosi nazwę gramatyk atrybutowych. Reguły takie są rozbudowywane o fragmenty wstawiające kawałki kodu do generowanego programu przeprowadzające kontrolę dynamiczną.

  22. Warianty (unie) i wartości zerowe • Warianty lub unie (variants, unions) i wartości zerowe (null values) są nieregularnościami w strukturach danych. Warianty służą do odwzorowania sytuacji w modelowaniu pojęciowym kiedy wystąpienia danej określonego typu mogą się różnić zestawem lub typem atrybutów. Np. : • Pracownik:( Nazwisko:"Nowak", Rodzaj:"etatowy", Zarobek:3000 ) • Pracownik:( Nazwisko:"Wrona", Rodzaj:"uczeń", Status:3, Stypendium:700 ) • Obiekt pracownika Nowaka nie musi posiadać atrybutów Status i Stypendium, dzięki czemu zajmuje mniej pamięci. Tego rodzaju sytuacja jest modelowana jako „zapis z wariantami” (w rodzinie języków linii Pascala) lub unia (w rodzinie C i C++), np. (w składni C): • struct{ string Nazwisko; string Rodzaj; • union{ int Zarobek; • struct{ int Status; int Stypendium;} str; • } un; • } Pracownik; Warianty mogą posiadać atrybut (taki jak Rodzaj), który służy do rozróżnienia przypadku podczas czasu wykonania. Taki atrybut nazywa się dyskryminatorem.

  23. Wartości zerowe • Pojęcie wartości zerowej służy do podobnego celu jak pojęcie wariantu, chociaż jest koncepcyjnie nieco różne. Istnieje wiele powodów dla wartości zerowych, np.: • Informacja jest nierelewantna, np. atrybut NazwiskoPanieńskie dla mężczyzn. • Informacja jest relewantna, lecz nie została jeszcze zapełniona („niezainicjowana zmienna”). • Informacja jest ważna tylko przez pewien czas. • Informacja jest znana i może być zapełniona, ale dla pewnych przypadków jest nieistotna. • Przy adaptacji heterogenicznych baz danych dane składają się ze starszych i młodszych zapisów, przy czym młodsze zawierają informacje wynikające z nowych wymagań na system; starsze zapisy tej informacji nie posiadają. • Warianty są z należytym respektem traktowane w językach programowania, natomiast wartości zerowe raczej nie; zwykle sprowadza się je do „wartości niezainicjowanej zmiennej” lub „zerowego wskaźnika”. W bazach danych ten stosunek jest dokładnie odwrotny: wartości zerowe uzyskały tam pierwszą kategorię obywatelstwa, podczas gdy o wariantach praktycznie się nie wspomina. • Z punktu widzenia poprawności typologicznej wartości zerowe implikują podobne problemy jak warianty. Trudności związane z wariantami i wartościami zerowymi spowodowały tendencję do ich eliminowania z języków programowania, np. z języków Java i Modula-3.

  24. Warianty potraktowane jako odrębne klasy Osoba Nazwisko Rodzaj Pracownik Zarobek Uczeń Status Stypendium Przy takim potraktowaniu wariantów moze sie również okazać, że dyskryminator (w przykładzie atrybut Rodzaj) jest zbędny, gdyż ta sama informacja jest już odwzorowana. W związku z możliwością zastąpienia wariantów przez hierarchię klas spotykane są poglądy, że pojęcie wariantu jest zbędne, a nawet sprzeczne z obiektowością. Zwrócimy uwagę, że warianty i wartości zerowe są cechą techniczną, która może mieć znaczenie dla modelowania pojęciowego. Brak wariantów i wartości zerowych powoduje wiele niekorzystnych konsekwencji dla modelu pojęciowego i modelu implementacyjnego, w szczególności, niepotrzebne zwiększenie (eksplozję) liczby klas, konieczność wielokrotnego dziedziczenia, trudności związane z aktualizacją obiektów (zmiana wariantu obiektu oznacza zmianę klasy do której obiekt należy), itd.

More Related