I.
Wprowadzenie do  języków zapytań (1)
II.
Wprowadzenie do  języków zapytań (2)
III.
Pojęcia obiektowości w bazach danych (1)
IV.
Pojęcia obiektowości w bazach danych (2)
V.
Podstawy semantyczne języków zapytań
VI.
Modele składu obiektów
  Wstęp
  1. Założenia składniowe języka SBQL
  2. Abstrakcyjny model składu obiektów
  3. Modele M0, M1, M2 i M3 składu obiektów
  Podsumowanie
  Zadania
VII.
Stos środowisk, rezultaty zapytań, funkcja nested
VIII.
Język SBQL (Stack-Based Query Language) (1)
IX.
X.
Dalsze własności SBQL
XI.
Operatory order by i group by
XII.
Przetwarzanie struktur nieregularnych
XIII.
Rozszerzenie języków zapytań o konstrukcje imperatywne
XIV.
Procedury, procedury funkcyjne, metody, reguły zakresu
XV.
Parametry procedur i metod, procedury rekurencyjne, optymalizacja poprzez modyfikację zapytań

 

3. Modele M0, M1, M2 i M3 składu obiektów


3.1. Model składu M0

Każdy obiekt posiada unikalny wewnętrzny identyfikator, zewnętrzną nazwę, oraz wartość, która może być atomowa, pointerowa lub złożona. Obiekty zdefiniujemy więc poprzez następującą definicję:

  • Obiektem jest trójka <i, n, v>. Taki obiekt będziemy nazywać obiektem atomowym.

  • Obiektem jest trójka <i1, n, i2>. Taki obiekt będziemy nazywać obiektem pointerowym lub referencyjnym. Obiekt taki jest identyfikowany przez i1, natomiast i2 jest jego wartością, która jest pointerem (inaczej referencją) do innego obiektu.

  • Obiektem jest trójka <i, n, T>, gdzie T jest zbiorem dowolnych obiektów. Taki obiekt będziemy nazywać obiektem złożonym. Powyższa reguła jest rekurencyjna, czyli umożliwia tworzenie obiektów o nieograniczonej złożoności i o nieograniczonej liczbie poziomów hierarchii.
W modelu M0 skład obiektów jest zdefiniowany jako para <S, R>, gdzie S jest zbiorem obiektów, zaś R jest zbiorem identyfikatorów określanych przez nas jako "identyfikatory startowe".

Zbiór R wyznacza punkty wejściowe do składu obiektów, tj. takie obiekty, które mogą być początkiem nawigacji w zbiorze przechowywanych obiektów. Zwykle są to identyfikatory obiektów będących na górnym poziomie hierarchii obiektów, tj. takich, które nie są zawarte w innych obiektach. Jeżeli jednak przyjmiemy, że nasz skład na górnym poziomie hierarchii zawiera moduły lub ekstensje (które, jak zobaczymy, mieszczą się w tej definicji), wówczas zbiór R może zawierać niekoniecznie identyfikatory obiektów z górnego poziomu hierarchii.

23
Rys.23. Model M0 - mała baza danych i jej diagram klas

Skład obiektów podlega pewnym ograniczeniom, wśród nich następującym:

  • Każdy obiekt, podobiekt itd. w składzie posiada unikalny identyfikator.

  • Jeżeli (na dowolnym poziomie hierarchii obiektów) wystąpi obiekt pointerowy <i1,n,i2>, to powinien istnieć również obiekt posiadający identyfikator i2. Warunek oznacza brak zwisających pointerów  (lub tzw. integralność referencyjną).

  • Dowolny identyfikator ze zbioru R jest identyfikatorem pewnego obiektu znajdującego się w składzie.

  • Będziemy abstrahować od obiektów, które nie są osiągalne ze zbioru R, bezpośrednio lub pośrednio. Obiekt bezpośrednio osiągalny posiada identyfikator ze zbioru R. Obiekt jest osiągalny, jeżeli jest bezpośrednio osiągalny lub jest podobiektem obiektu osiągalnego. Obiekt jest także osiągalny, jeżeli posiada identyfikator i2 oraz jest osiągalny obiekt pointerowy <i1, n, i2>. Obiekty nieosiągalne nie mają znaczenia dla semantyki języka, ponieważ nie są w stanie wpłynąć na wynik ewaluacji zapytań; są tzw. nieużytkami (garbage) i mogą być w dowolnym momencie skasowane.


Niżej znajduje się przykład obiektu o nazwie Prac:

P6.2.

<i5, Prac, { <i6, Nazwisko, "Kowalski">,
                <i7, Zar, 2000 >,
                <i8, PracujeW, i22>}>

Obiekt posiada trzy podobiekty (w innych modelach nazywane atrybutami): obiekty atomowe o nazwach NazwiskoZar (Zarobek) oraz obiekt pointerowy o nazwie PracujeW prowadzący do obiektu o identyfikatorze i22. Każdy obiekt i podobiekt posiada swój unikalny identyfikator.

Przykład miniaturowej bazy danych jest pokazany na Rys.23 i Rys.24. Zawiera ona kilka obiektów PracDział powiązanych między sobą obiektami pointerowymi. Na Rys.24 kółkami zaznaczone są identyfikatory startowe.


3.2. Modelowanie zmiennych i danych nietrwałych

W definiowanych przez nas modelach składu obiektów nie będziemy robić rozróżnienia pomiędzy pojęciem języków programowania określanym jako "zmienna" a wprowadzonymi przez nas obiektami. Np. zmienna x z języka programowania posiadająca aktualną wartość 5 będzie w naszych modelach składu reprezentowana jako trójka <i, x, 5>, gdzie i jest pewnym identyfikatorem wewnętrznym tej zmiennej, np. jej adresem w pamięci operacyjnej. Nie będziemy również tworzyć rozróżnienia pomiędzy pojęciami zmienna i obiekt na zasadzie: obiekt posiada swoją klasę, zaś zmienna nie posiada klasy. W sensie koncepcyjnym to rozróżnienie nie jest jakościowe: np. można uważać, że podana przez nas zmienna x również ma swoją klasę, w której jest przechowywany jej typ. Można również przyjąć, że wszystkie obiekty posiadają klasę, ale dla niektórych obiektów są one puste (nie zawierają żadnych inwariantów), wobec czego są pomijane.

24
Rys.24. Model M0 - poglądowy obraz małej bazy danych z Rys.23

Model M0 (i pozostałe modele składu) nie zajmują się również cechą trwałości obiektów, tj. czy obiekty są przechowywane na dysku (dzięki czemu żyją dłużej niż wykonanie programu, który je utworzył) czy też w pamięci operacyjnej. Obiekty trwałe oraz obiekty ulotne są więc nierozróżnialne dla definiowanych przez nas języków zapytań. Cecha trwałości jest istotna i w implementacji trzeba będzie ją wprowadzić w ten czy inny sposób, ale nie ma najmniejszego wpływu na proponowaną przez nas definicję języka zapytań oraz pozostałych konstrukcji zdefiniowanych na bazie tego języka.


3.3. Relatywizm obiektów

W definiowanych przez nas modelach składu obiektów nie będziemy przywiązywać wagi do podziału obiektów na proste i złożone, a także nie wprowadzamy specjalnej terminologii i pojęć dla obiektów złożonych (takich jak "atrybut", "struktura", "krotka", "kolekcja" itd.). Wszystkie te pojęcia dadzą się zamodelować za pomocą opisanego modelu składu.  Rozróżnienie na obiekty, atrybuty, struktury itd. może być istotne dla modeli zewnętrznych (np. obiektowych diagramów klas używanych przy analizie i projektowaniu systemów), ale jest nieistotne dla definicji języka zapytań, która będzie opisana w tym wykładzie. W naszej koncepcji (podobnie jak w języku Smalltalk) obiekt, o ile jest złożony, składa się po prostu z obiektów; inne pojęcia składające się na obiekt nie występują.

Relatywizm obiektów ma zasadnicze znaczenie dla uproszczenia definiowanych języków, znacznie upraszcza metamodel i operacje na metamodelu, zwiększa uniwersalność języka i ma zasadnicze znaczenia dla jego prostoty oraz klarowności semantyki i pragmatyki. Te zalety z kolei mają zasadniczy wpływ na możliwość opracowania i zaimplementowania efektywnych i uniwersalnych metod optymalizacji zapytań.

W wielu koncepcjach obiektowości (np. w standardach CORBA i ODMG) relatywizm nie jest wyznawany. Np. w ODMG atrybut jest tzw. literałem, który nie jest obiektem. Podobnie, większość koncepcji innych autorów implicite zakłada, że dowolny obiekt musi być złożony, tj. musi posiadać strukturę wewnętrzną w postaci atrybutów, pól itp. W naszym podejściu odcinamy się od tych koncepcji. Relatywizm obiektów oznacza, że dowolny obiekt może stać się podobiektem innego obiektu (czyli być jego "atrybutem") oraz że obiekty atomowe są również obiektami.

W tej koncepcji zbędne również staje się pojęcie modułu. Moduł jest po prostu obiektem składającym się z obiektów. Cechy operacyjne modułów, takie jak możliwość ich odrębnego przechowywania i kompilacji, nie są istotne na tym poziomie abstrakcji, na którym będziemy definiować semantykę języków zapytań.


3.4. Modelowanie kolekcji i struktur

W zdefiniowanym powyżej modelu M0 (jak i w następnych modelach) nie zakładamy unikalności zewnętrznych nazw obiektów. Dotyczy to dowolnego poziomu hierarchii obiektów. Przykładowo, na górnym poziomie hierarchii nazwy PracDział nie są unikalne, zaś wewnątrz obiektów Dział nie są unikalne nazwy LokacjaZatrudnia. Jak widać na przykładzie z Rys.23 i Rys.24, to założenie umożliwia modelowanie kolekcji bez wprowadzania w tym celu specjalnych środków formalnych. Podobne założenie odnośnie do kolekcji przyjmuje także język XML. Ponadto abstrahujemy od wielu pojęć wprowadzanych w innych modelach, takich jak krotki (tuples), struktury, warianty/unie, zapisy (records), zbiory (sets), bagi (bags), ekstensje (extents) itd. W procesie budowy abstrakcyjnego modelu uznaliśmy, że z pozycji naszego celu, czyli budowy semantyki języka zapytań, wszystkie te pojęcia dadzą się wyrazić w terminach podanego modelu poprzez pewne ograniczenie lub specjalizowanie jego konstrukcji. Z naszego punktu widzenia są to pewne zestawy obiektów (w naszej terminologii - "środowiska"). Nie uznaliśmy za potrzebne specjalizowanie tych kolekcji i kwalifikowanie ich do specjalnych kategorii. Dzięki takiemu uproszczeniu jesteśmy w stanie wyjaśnić tę semantykę w ogólnych terminach, bez konieczności rozpatrywania dużej liczby przypadków szczególnych, które z punktu widzenia opisu semantyki często niewiele się różnią. W ten sposób, poprzez zastosowanie relatywizmu obiektów, mamy również znacznie większą kontrolę nad uniwersalnością naszej struktury danych oraz definiowanego na tej bazie języka zapytań.  Jest to również zasadnicza zaleta z punktu widzenia optymalizacji zapytań.

W naszych modelach składu kolekcja nie występuje jako pojedynczy, identyfikowalny byt programistyczny.

W szczególności, nie będzie można zbudować referencji do kolekcji jako takiej, ale będzie można zbudować zbiór referencji do elementów danej kolekcji, w szczególności do wszystkich elementów danej kolekcji. Również nie można będzie w naszym składzie założyć kolekcji, która jest (na razie) pusta. W modelu relacyjnym oznaczałoby to, że nie można w bazie danych założyć pustej tablicy, co brzmi jak przyznanie się do poważnej wady. Można jednak dyskutować, czy jest to rzeczywiście wada, a ponadto wady tej można łatwo uniknąć poprzez dodatkowe założenia formalne. Oczywiście, można zbudować odrębny nadobiekt (o własnej nazwie i własnym identyfikatorze), którego wartością będą obiekty tworzące kolekcję, tak jak pokazano na Rys.22. W szczególności, taki złożony obiekt może być pusty, tj. nie zawierać podobiektów.

Założenia przyjęte przy budowie modelu M0 pozwalają nam na uniknięcie drobnej wady koncepcyjnej (występującej np. w standardzie ODMG), kiedy twórcy danego modelu nie mogą zdecydować się w jednoznaczny sposób, czy np. używać nazwy Osoba, identyfikującej pojedynczy obiekt Osoba lub pewien zbiór obiektów Osoba, czy raczej nazwy w liczbie mnogiej Osoby, której intencją jest identyfikacja całej kolekcji (ekstensji) nazwanej Osoby. Prowadzi to do niejasności, czy należy w zapytaniach używać np.:

P6.3.
OQL:

select x.Nazwisko from Osoba as x where ... (liczba pojedyncza)

czy może raczej

P6.4.
OQL:

select x.Nazwisko from Osoby as x where . (liczba mnoga)

Która z tych form jest poprawna, czy prowadzą do różnego wyniku, czy być może są równoważne? Przypominamy zasadę, że każdy, nawet najdrobniejszy problem semantyczny należy traktować jako duży problem, szczególnie, jeżeli celem jest standard języka.

Model formalny omawiany w tym wykładzie będzie zajmował się prawie wyłącznie kolekcjami zwanymi "bagami" (bags). Czasami będziemy zajmować się sekwencjami, gdyż na nich działają ważne operatory, ale pojęcie sekwencji wymaga rozszerzenia wprowadzonych przez nas modeli o cechy, które są drugorzędne z punktu widzenia definicji semantyki. Jakkolwiek użyteczność sekwencji jest wysoka, wykład ten ma objaśnić zasady konstrukcji semantyki języka zapytań, a nie precyzyjnie reguły konkretnego języka. Jesteśmy zdania, że projektanci, którzy zrozumieją te zasady, bez trudu rozszerzą je na sekwencje. Na przykładzie tablic - struktur danych podobnych do sekwencji - pokażemy, w jaki sposób można dokonać tego rodzaju rozszerzenia.


3.5. Modelowanie powiązań pomiędzy obiektami

Obiekty pointerowe umieszczone wewnątrz obiektów są w stanie odwzorować powiązania pomiędzy obiektami. Na Rys.23 i Rys.24 każdy obiekt pointerowy PracujeW prowadzi do odpowiedniego obiektu Dział, zaś każdy obiekt pointerowy Zatrudnia prowadzi do obiektu Prac. Na razie nie będziemy troszczyli się o to, że te pointery są redundantne (powiązane dodatkowym ograniczeniem), ponieważ na tym etapie nie interesuje nas schemat składu obiektów lub jego zmiany, a wyłącznie jego stan.

Podobnie jak w modelu ODMG, nie będziemy interesowali się powiązaniami o arności wyższej niż 2 oraz powiązaniami posiadającymi własne atrybuty. Powodem tego (niezbyt dokuczliwego) ograniczenia jest to, że powiązania ternarne, 4-arne itd. wprowadzają trudności interpretacyjne w zakresie ich użycia. Jedyną znaną nam koncepcją wprowadzającą do języka programowania pojęcie związku n-arnego z atrybutami jest Relationship Service standardu CORBA. Według naszej wiedzy, ta usługa nie została nigdzie zaimplementowana ze względu na zbyt dużą złożoność programistycznego interfejsu. W szczególności, trudności są związane z aktualizacją takich powiązań, np. przełączeniu jednej gałęzi danego powiązania do innego obiektu. Powiązania posiadające własne atrybuty prowadzą do niejasności koncepcyjnej. Mianowicie, jeżeli są zdefiniowane atrybuty powiązań, to musi także gdzieś istnieć opis typologiczny tych atrybutów, a także muszą istnieć metody przypisane do powiązań, gdyż niektóre atrybuty nie mogą obejść się bez metod. Zatem konieczne byłoby  tworzenie nowego rodzaju klasy, która opisywałaby podane wyżej inwarianty powiązań. Ergo, istniałyby dwie koncepcje klas: klasy dla obiektów i klasy dla powiązań. Nie byłoby łatwo objaśnić użytkownikom, jaka jest różnica pomiędzy takimi dwoma rodzajami klas. W tej sytuacji, biorąc jednocześnie pod uwagę wspomniane problemy koncepcyjne z aktualizacją powiązań oraz miejscem przechowywania inwariantów powiązań, najwygodniej byłoby takie złożone powiązanie potraktować jako normalny obiekt. To zaś oznacza powrót do punktu wyjścia, czyli rezygnacji z powiązań n-arnych (ew. z atrybutami) na rzecz wprowadzenia nowej klasy obiektów oraz zastąpienia takiego powiązania przez n powiązań binarnych (bez atrybutów). Ten zabieg jest zilustrowany na Rys.12.


3.6. Wartości zerowe,  warianty, dane półstrukturalne

Podany model umożliwia formalizację wartości zerowych i wariantów (unii). Na Rys.24 atrybut (złożony) Adres występuje tylko dla jednego obiektu Prac. Model niczym nie zobowiązuje nas do tego, aby obiekty posiadające te same nazwy posiadały podobiekty o tych samych nazwach. Inaczej mówiąc, model nie wprowadza (jak dotąd) ograniczeń typologicznych, co daje pełną swobodę w zakresie budowy poszczególnych obiektów, w szczególności, odwzorowanie wartości zerowych i wariantów. Będziemy uważać, że ewentualny dyskryminator wariantu jest takim samym podobiektem jak pozostałe. Może on być wykorzystany przez mechanizm mocnej kontroli typów, ale nie ma znaczenia dla semantyki języka zapytań.

Zatem wprowadzony przez nas model przykrywa nieregularności w danych, określane ostatnio jako dane półstrukturalne (semistructured). Oznacza to, że budowana przez nas semantyka będzie adekwatna do budowy języków zapytań i języków programowania obsługujących takie struktury. W szczególności, semantyka będzie nadawała się do struktur rozważanych w technologiach opartych na XML. Nie będziemy jednak naśladować innych autorów w mało rozsądnym i koniunkturalnym dążeniu do dopasowania lukru syntaktycznego projektowanego języka zapytań do lukru syntaktycznego XML.


3.7. Typy

Określanie typów dla obiektów oraz mocna statyczna kontrola typówkontrola typów są zagadnieniami bardzo istotnymi. Typy są dodatkowym ograniczeniem na budowę obiektów oraz są ograniczeniem kontekstu, w którym odwołania do obiektów mogą być użyte w zapytaniach lub programach. Mechanizm wymuszający te ograniczenia może być zbudowany na wierzchołku podstawowych mechanizmów semantyki języka (z pewnymi wyjątkami, które przedyskutujemy przy omawianiu mechanizmu stosu środowisk i wiązania). Zatem kompletny opis semantyki języka zapytań może (prawie) całkowicie obejść się bez pojęcia typu. Zachodzi tu pełna analogia z modelem relacyjnym, gdzie semantyka podstawowych operatorów języka zapytań (selekcji, projekcji, złączenia) nie wymaga wprowadzania typów. Również SQL w swojej warstwie semantycznej nie zakłada kontroli typów.

Typy są uważane za istotne również z punktu widzenia reprezentacji obiektów. W klasycznych językach programowania typy przechowują nazwy podobiektów (atrybutów) oraz informację o sposobie reprezentacji wartości elementarnych (np. rozmiar stringu w znakach). W naszym modelu informacja o wszelkich nazwach (obiektów, podobiektów itp.) jest podana explicite. Zakładamy również, że informacja o reprezentacji wartości atomowej (czy jest to np. integer, real, string, grafika JPEG itd.) jest podana razem z tą wartością lub wynika z innych cech (np. z nazwy obiektu lub właśnie z jego typu), które dla naszych rozważań są nieistotne. Dodatkowym powodem niezajmowania się przez nas typami jest fakt, że typy dla języków zapytań są dziedziną stosunkowo dobrze opanowaną i zaimplementowaną w wielu systemach.

Jakkolwiek typy przesądzają, czy dane zapytanie jest poprawne czy niepoprawne, definicja semantyki poprawnych zapytań mogłaby się w zasadzie obyć bez wprowadzania pojęcia typu. Jak się jednak okaże, nie jest to do końca prawda w przypadku nieregularności w danych (np. wartości zerowych lub wariantów) i języka zezwalającego na zagnieżdżanie zapytań. Do tej kwestii wrócimy w dalszej części książki.


3.8. Identyfikatory obiektów

Każdy identyfikator obiektu znajdującego się w składzie obiektów powinien być unikalny. Powinien być również trwały; nie może zmieniać się w czasie życia obiektu. To założenie może być nieco osłabione: identyfikator obiektu może być zmieniony, o ile mamy gwarancję, że nie istnieją odwołania do tego obiektu (z innych obiektów lub z aktualnie działających programów) lub wtedy, gdy zmiana identyfikatora obiektu pociąga za sobą jednoczesną zmianę wszystkich odwołań do tego obiektu poprzez ten identyfikator.

Jak powiedzieliśmy wcześniej, nie interesują nas zasady budowy identyfikatorów. W szczególności, mogą one być adresami dyskowymi, adresami w pamięci operacyjnej, logicznymi identyfikatorami obiektów. Dla podobiektów (atrybutów) identyfikatorem może być np. para <id_obiektu, offset>, gdzie offset jest przesunięciem początku wartości podobiektu względem początku obiektu, lub para <id_obiektu, nazwa atrybutu>. Zasadniczym powodem wprowadzenia przez nas identyfikatorów jest umożliwienie tworzenia referencji do dowolnych obiektów. W naszym podejściu pojęcie referencji jest kluczowe, wykorzystywane we wszystkich regułach semantycznych. Istotą referencji jest to, aby jednoznacznie i prosto identyfikowała pewien obiekt lub pewną wartość znajdującą się w składzie. Budowa identyfikatora (i co za tym idzie,referencji) jest jednak zasadnicza przy implementacji języka i może wpływać na możliwości bazy danych, języka zapytań i wydajność procesora zapytań .


3.9. Wartości atomowe

Jakkolwiek w podanych przez nas przykładach wartości atomowe są zwykle stringowe lub numeryczne, nie wprowadzamy w tym zakresie jakichkolwiek ograniczeń. Wartością atomową może być dowolnie długa wartość tekstowa, tekst w pewnym specyficznym formacie, mapa bitów, wartość reprezentująca grafikę, dźwięk, wideo itd. Z naszego punktu widzenia nie jest istotna długość wartości atomowej ani też fakt, czy jest zmiennego, czy też stałego formatu. Istotny jest wyłącznie fakt, że taka wartość z punktu widzenia semantyki języka jest niepodzielna; nie można w niej wyróżnić identyfikowalnych części posiadających własne nazwy i identyfikatory. Jeżeli takie części dają się wyróżnić, wówczas jest to wskazówka, że nie jest to wartość atomowa. Np. rozpatrując adres pewnego miejsca, jest on atomowy tylko wtedy, jeżeli jego całość będziemy traktować jako ciąg znaków. Jeżeli natomiast będziemy chcieli w zapytaniach dostawać się do jego części, np. nazwy miasta, nazwy ulicy, numeru domu itd., wówczas taka wartość nie może być atomowa; powinna być odpowiednio rozdzielona na podobiekty posiadające własne nazwy, identyfikatory i wartości.

Wartościami atomowymi mogą być także procedury, funkcje, metody, perspektywy (views), wirtualne (pochodne) atrybuty, ograniczenia, bloki obsługi zdarzeń, reguły, procesy, wątki i inne elementy proceduralne, które mogą występować w zapytaniach lub wpływać na przebieg ich ewaluacji. Z punktu widzenia naszego modelu składu biektów,różnica np. pomiędzy wartością typu string a wartością typu procedura jest drugorzędna, ponieważ obie są wartościami atomowymi zajmującymi pewne miejsce w składzie obiektów. Oczywiście, zasadnicza różnica dotyczy faktu, że na wartości stringowej można wykonać np. operację konkatenacji, której nie da się wykonać na wartości typu procedura, i odwrotnie, na wartości typu procedura można wykonać np. operację wywołaj, która nie stosuje się do wartości stringowej. Niemniej z punktu widzenia semantyki języka zapytań obie te wartości mogą być do pewnego stopnia traktowane na tych samych prawach. Podobny stosunek do elementów proceduralnych ma również współczesna teoria typów.

Obiekt atomowy < i, n, v > przechowujący metodę, funkcję lub procedurę posiada unikalny wewnętrzny identyfikator i na tych samych zasadach, jak dla innych obiektów. Nazwa n jest nazwą tej metody, funkcji lub procedury, zaś wartość v jest jej wykonywalnym kodem (lub tzw. ciałem).

Przyjęcie powyższych założeń niekoniecznie oznacza, że ciałem metody, funkcji lub procedury itd. jest pewien skrypt, który jest interpretowany (dynamicznie lub późno wiązany). W systemie Loqis przyjęliśmy, że wewnątrz tego v znajduje się zarówno kod źródłowy (który może być wyświetlony i zmieniony przez pewien edytor tekstowy), jak i kod skompilowany (w którym występują bezpośrednie (już związane) odwołania do bytów programistycznych czasu wykonania). W zapytaniach nie można tych dwóch kodów rozdzielić, wobec czego oba stanowią jedną atomową wartość (do której można stosować różne operacje, np. edytuj, kompiluj lub wykonaj). Oczywiście można sobie wyobrazić także sytuację z wczesnym statycznym wiązaniem, kiedy w rzeczywistości kod (czyli nasze v) jest przechowywany w pamięci operacyjnej na normalnych zasadach, zaś w składzie wartość v oznacza wyłącznie adres maszynowy (lub inny identyfikator) tego kodu. Tego rodzaju konfiguracje będziemy uważać za optymalizacje niższego poziomu, które nie mają wpływu na nasz abstrakcyjny pogląd na skład obiektów.


3.10. Niezależność modelu od cechy trwałości

Jakkolwiek temat języków zapytań jest jak dotąd ściśle wiązany z tematyką baz danych, ten związek nie jest oparty na trwałych podstawach. Związek pomiędzy językami zapytań abazami danych pojawił się stąd, że bazy danych przechowywały wyłącznie kolekcje, natomiast języki zapytań są przeznaczone do operacji na kolekcjach. Z kolei w językach programowania kolekcje są tematem marginalnym, co powoduje, że języków zapytań nie rozpatruje się w społeczności zajmującej się językami programowania.

W ostatnim czasie coraz więcej zwolenników zdobywa koncepcja ortogonalnej trwałości (orthogonal persistence), która oznacza brak różnic w systemie typów dla trwałych i ulotnych danych (lub obiektów). Przyjęcie tej koncepcji oznacza, że kolekcje mogą być zarówno trwałe, jak i ulotne, a to z kolei oznacza, że języki zapytań w jednakowym stopniu stosują się do jednej i drugiej kategorii danych, czyli zarówno do baz danych, jak i do języków programowania. Językiem tego rodzaju jest Data Base Programming Language - DBPL, opracowany i zaimplementowany na Uniwersytecie w Hamburgu. DBPL rozszerza język Modula-2 zarówno o kolekcje, jak i o trwałe dane. Bazuje na koncepcji ortogonalnej trwałości i posiada język zapytań zintegrowany ze standardowymi wyrażeniami Modula-2. Autor tej książki uczestniczył w projekcie oraz implementacji tego języka (jakkolwiek zbudowany tam język zapytań nie był oparty na podejściu prezentowanym w tym wykładzie). Również system Loqis zaprojektowany i zaimplementowany przez autora, jest całkowicie oparty na koncepcji ortogonalnej trwałości.

Przyjęty przez nas model składu obiektów nie włącza cechy trwałości, gdyż semantyka języka zapytań jest od tej cechy niezależna. Zakładamy więc, że cecha trwałości jest obsługiwana przez inne konstrukcje językowe, nie mające związku z językiem zapytań.


3.11. Model relacyjny i model zagnieżdżonych relacji

Jak łatwo się przekonać, zaprezentowany wyżej model M0 włącza struktury danych zakładane przez model relacyjny jako szczególny przypadek, Rys.25 i Rys.26. Semantykę relacyjnego języka zapytań (w szczególności SQL) można będzie zdefiniować jako szczególny przypadek definiowanej przez nas semantyki.

25
Rys.25. Struktura relacyjna reprezentowana przez model M0

Nie oznacza to oczywiście, że będziemy nastawiać się na definiowanie semantyki SQL. SQL jest językiem o licznych anomaliach, niekonsekwencjach i semantycznych rafach, w związku z tym definiowanie jego precyzyjnej semantyki jest trudne i mało sensowne. Jesteśmy zdania, że przed taką definicją należałoby wcześniej uporządkować koncepcję języka, a na to w przypadku SQL jest za późno. Chodzi nam więc raczej o to, że na podstawie rozwijanego przez nas podejścia można definiować semantykę relacyjnych języków zapytań na odpowiednim poziomie precyzji.

Nasz model składu obiektów przykrywa model zagnieżdżonych relacji (NF2) jako szczególny przypadek. Również struktury danych implikowane przez inne modele, określane przez ich autorów jako funkcjonalne, obiektowe, logiczne, semantyczne itd. dadzą się sformalizować w terminach podanego prostego modelu. Jest więc czymś w rodzaju ramy formalnej nie wprowadzającej a priori ograniczeń ideologicznych, a jednocześnie dostatecznie mocnej, aby być podstawą formalizacji języków zapytań.

26
Rys.26. Poglądowy obraz struktury relacyjnej z Rys.25

Zwrócimy uwagę, że model M0 jest semantycznie bogatszy od modelu relacyjnego. Np. w klasycznym sformułowaniu modelu relacyjnego nie jest możliwe zdefiniowanie operatora naturalnego złączenia (natural join), ponieważ taka definicja wymaga użycia nazw atrybutów, a te nazwy nie są własnością relacji, lecz nieformalnego metajęzyka zapisu relacji. W przypadku M0 ten problem nie istnieje, gdyż nazwy obiektów są na tym samym poziomie pojęciowym, co wartości obiektów.


3.12. Modelowanie plików XML

W swoich początkach XML był postrzegany jako "lepszy HTML", którego zadaniem było ustrukturalizowanie informacji znajdujących się na Webie, czyli oddzielenie semantyki danych od formy jej wyświetlania na ekranie przeglądarki. Ten cel został praktycznie osiągnięty, jakkolwiek skala zastępowania plików HTML przez pliki XML/XSL nie wydaje się na razie znacząca, tym bardziej że w większości obecnie strony HTML są generowane na podstawie informacji zapisanych w bazach danych podtrzymywanych przez popularne SZBD. Kolejnym celem XML stało się zestandardyzowanie typowych informacji w poszczególnych dziedzinach (bibliografie, informacje giełdowe, rozkłady lotów itd.). Realizacja tego celu wymagałaby podjęcia prac standardyzacyjnych na ogromną skalę. W rzeczywistości, skala tych prac jest również ograniczona, zważywszy również na to, że w ważniejszych dziedzinach istniały już wcześniej standardy elektroniczne nie oparte na XML (np. EDI). Podobnym lub prawie równoważnym celem było zestandaryzowania formatów wymiany typowej informacji; cel ten jest również realizowany, ale na dość ograniczoną skalę.

Ostatnie nastawienie społeczności internetowej traktuje pliki XML jako struktury danych, z wyraźnym nawiązaniem do technologii bazodanowych, takich jak języki zapytań. Wytwarzany jest pewien klimat nowości w tym zakresie, XML jest wychwalany jako wszechstronny standard, który rozwiązuje lub obiecuje rozwiązać wiele dotychczas nie rozwiązanych problemów.

27
Rys.27. Reprezentacja pliku XML w M0

Doceniając znaczenie standardyzacji w obrębie Internetu, autor jest zdania, że hałas dookoła XML jest sztuczny i ociera się o techniczny banał i koncepcyjną płyciznę. Większość twórczości dotyczącej styku XML z bazami danych jest niewarta czytania. Hałas jest istotny dla zarabiania pieniędzy: twórcy oprogramowania oraz właściciele firm programistycznych dostrzegli w XML nową niszę rynkową. Z tego względu XML może odegrać znaczną rolę dla osób pragnących zarobić duże pieniądze. Natomiast z naukowo-technicznego punktu widzenia, XML, jako model danych jest krokiem w tył w stosunku do obiektowych baz danych. XML nie wnosi niczego do wiedzy z zakresu baz danych, języków programowania i struktur danych. Jest to tylko pewna uproszczona konwencja syntaktyczna dotycząca struktury plików tekstowych, cieniutki podkład syntaktyczny pod obszerne technologie, które mogłyby się na tej bazie rozwinąć. Konwencja ta implikuje hierarchiczne struktury danych, wobec czego jej autentyczną zaletą jest to, że zrobiła znaczący wyłom w poglądach świata przemysłowego na organizację danych, gdzie obowiązywał stereotyp systemów relacyjnych i SQL.

Autor jest zdania, że XML powinien pozostać na obrzeżach repozytoriów danych. Pliki XML mogą być wejściem i wyjściem z takich repozytoriów i tutaj rola XML jako metastandardu może być znacząca. Do tego celu konieczny jest znaczny wysiłek standardyzacyjny, polegający na uzgodnieniu dla danej dziedziny przedmiotowej konkretnej organizacji struktury XML i jej znaczenia, włączając nazwy wpisywane w tagach, semantykę i biznesową ontologię wartości itd. XML nie powinien być jednak wpuszczany do wnętrza baz i repozytoriów danych, gdyż wprowadza ograniczenia techniczne i koncepcyjne, które są nieakceptowalne dla większości koncepcji przyszłych baz danych.

Konsekwencją przytoczonego wyżej poglądu jest teza, że specjalne języki zapytań dla XML są mało sensowne, gdyż ich przedmiotem są dane przechowywane w repozytoriach, a nie pliki tekstowe zapisane w konwencji XML. Języki zapytań dla XML, takie jak XQL, Xpath, Xlink są zdecydowanie poniżej stanu sztuki [Fern00]. Język XQuery [XQue04] jest w tym względzie znacznym postępem, jakkolwiek razi nieco chaotyczność i przypadkowość jego konstrukcji oraz brak solidnych podstaw jego semantyki. Teorie powstałe w tym celu, takie jak specjalne "algebry dla XML" są niedojrzałe i nie mogą spełnić innej roli niż bycie tzw. "naukową dekoracją".

Specjalne repozytoria danych dla XML (native XML repositories, XML managers) są również mało sensowne z technicznego (nie marketingowego) punktu widzenia, gdyż ograniczanie struktur baz danych do struktur izomorficznych z hierarchiczną strukturą pliku XML nie ma uzasadnienia i prowadzi do wielu niepotrzebnych ograniczeń dla programisty aplikacyjnego. O tym, że hasło "XML" ma znaczenie dekoracyjne, świadczy fakt, że wiele firm swoje stare bazodanowe produkty zaczęło nazywać "XML managers" nie zmieniając ich zasadniczej konstrukcji. Przykładem jest obiektowy system zarządzania bazą danych ObjectStore "urodzony na nowo" jako zarządca XML o nazwie Excelon. Podobnie jest z bazą danych Tamino firmy Software AG. Rzeczą ważną jest ustalenie standardowego interfejsu do programowania aplikacji (API, Application Programming Interface) do tych baz danych. Niestety, prace nad technologią XML nie potoczyły się w tym kierunku (poza ograniczonym modelem DOM), a to oznacza dla programisty, że każdy z tych systemów dostarcza własne API, czyli standardyzacja aplikacji działających na takich repozytoriach jest tak czy inaczej niemożliwa, mimo standardu XML.

XML ustala pośrednio hierarchiczne struktury danych, i one właśnie (a raczej ich uogólnienie) powinny być przedmiotem zarządzania wewnątrz repozytoriów danych. XML wprowadza wiele ograniczeń, które są nieistotne dla definicji języków zapytań; zatem powinny być konstruowane dla bardziej generalnych struktur danych.

Dowolny plik XML może być zamieniony na strukturę danych w konwencji modelu M0. Rys.27 przedstawia przykładowy plik XML i jego odpowiednik w M0.

Kilka kwestii związanych z XML wymagałoby uściślenia. Pierwszą z nich są tzw. atrybuty pisane wewnątrz tagów XML. Model DOM traktuje je jako jeden string dostępny dla programisty poprzez specjalne opcje. Nie wydaje się to właściwe. Jakkolwiek atrybutom przypisuje się pewne szczególne znaczenie - są to tzw. metadane, opisujące dane - granica pomiędzy danymi a metadanymi jest rozmyta. Z punktu widzenia języka zapytań nie ma powodu, aby metadane były inaczej traktowane niż regularne dane. Przykładowo, jeżeli mamy następujący plik XML:

P6.5.

<pracownik wersja = "1.2" wprowadzono = "24.03.03"
wprowadzajcy = "Marek">
      <imie>Jan</imie> .
</pracownik>

gdzie wersja, wprowadzonowprowadzający są atrybutami wewnątrz tagu pracownik, to z punktu widzenia języka zapytań atrybuty te powinny być dostępne w ramach tej samej składni. Można to zrobić na wiele sposobów. Wewnątrz repozytorium dokumentów XML budowanego według modelu składu M0 można wprowadzić podobiekt o wyróżnionej nazwie atrybuty, który zawierałby wszystkie atrybuty; przykładowo:

P6.6.

S - Obiekty:
< i1, pracownik, {
      < i11, atrybuty, { < i12, wersja, "1.2">,
                               < i13, wprowadzono, "24.03.03">,
                               < i14, wprowadzający, "Marek"> }
      < i2, imię, "Jan">, ... }

R - Identyfikatory startowe:
i1


Jest to jeden z wielu sposobów potraktowania atrybutów XML w modelu składu i, w konsekwencji, w języku zapytań.

Drugą osobliwością XML jest możliwość łączenia w ramach jednego obiektu stringów oraz podobiektów, np.:

P6.7.

<pracownik>
      Jan Adam Kowalski, urodzony 1973-12-1
      <adres> Warszawa, ulica Sienna 67 </adres>
      Jego zarobek wynosi 2500.
</pracownik>

W tym przypadku musimy przyjąć, że stringi "Jan Adam Kowalski, urodzony 1973-12-1" oraz "Jego zarobek wynosi 2500." są opatrzone pewnymi z góry ustalonymi tagami, np. tagiem <text>, co czyni powyższy fragment XML równoważny następującemu:

P6.8.

<pracownik>
      <text> Jan Adam Kowalski, urodzony 1973-12-1</text>
      <adres> Warszawa, ulica Sienna 67 </adres>
      <text> Jego zarobek wynosi 2500. </text>
</pracownik>


Trzecią kwestią związaną z XML jest niespełnienie omawianej wcześniej zasady wewnętrznej identyfikacji, czyli brak identyfikatorów dokumentów XML. To m.in. w istotnym stopniu utrudnia reprezentację związków pointerowych pomiędzy dokumentami XML oraz generalnie wszelkich opcji semantycznych, które wymagają referencji do obiektów XML. Rozwiązanie zastosowane w Xpath [XML04] polega na identyfikacji poddokumentu poprzez sekwencję nazw nawigującą w dół hierarchii obiektów XML. Ta opcja jest zmieszana z dużą liczbą innych opcji. Niestety, nie jest dobrą podstawą do budowy referencji do (pod)dokumentów XML o ile przyjmiemy założenie, że pliki XML mogą podlegać aktualizacji. Przykładowo, jeżeli wyrażenie Xpath prowadzi do trzeciego adresu instytucji, zaś podczas aktualizacji ten adres zostanie usunięty, wówczas - bez wiedzy programisty - to wyrażenie będzie prowadzić do adresu, który dotąd był czwarty (a teraz stał się trzeci). Wadliwość takiego rozwiązania jest oczywista.

Czwartą kwestią jest kolejność elementów, która w przypadku XML może być istotna (może przenosić informację). Tę kolejność, o ile jest potrzebna, w przypadku modelu M0 mogą również przenosić tagi zawierające liczby naturalne, np.:

P6.9.

<pracownik>
      <1>
            <text>Jan Adam Kowalski, urodzony 1973-12-1</text>
      </1>
      <2> <adres> Warszawa, ulica Sienna 67 </adres> </2>
      <3> <text> Jego zarobek wynosi 2500. </text> </3>
</pracownik>

Ten sposób wpłynąłby jednak na formułowanie zapytań, zatem należałoby go unikać. Nieco lepszym sposobem byłoby rozszerzenie modelu M0 poprzez wprowadzenie do niego (oprócz zbioru podobiektów) sekwencji podobiektów; patrz następna podsekcja.


3.13. Tablice i sekwencje

Istnieją ważne operatory, które potrzebują uwzględnienia porządku w danych (obiektach). Do nich należy np. operator order by języka SQL, jak również operatory takie jak daj_pierwszy, daj_następny, czy_ostatni itd. Istotny jest również operator wyboru n pierwszych (lub ostatnich) elementów z pewnej kolekcji. Umożliwia on m.in. takie zapytania jak "Podaj 50-ciu najlepiej zarabiających pracowników". Dla tego rodzaju operatorów potrzebne jest wzbogacenie naszego modelu formalnego o pojęcie sekwencji. Do tego celu należałoby zbudować nowy model, który odpowiednio rozszerzyłby modele M0 - M3.

Sekwencje obiektów mogą być modelowane w języku poprzez dodatkowe podobiekty (atrybuty) przechowujące liczby naturalne wyznaczające kolejność obiektów. Podobnie, można zastosować konwencję, w której nazwy obiektów są liczbami naturalnymi. Np. tablica ustalająca dzieci pracownika w porządku od najstarszego do najmłodszego mogłaby mieć postać:

P6.10.

<i1,Dzieci, {<i2,1, "Jacek">, <i3,2, "Adam">, <i4,3, "Anna">}>

Przy takim modelu dostęp do elementu tablicy następowałby poprzez indeks, np. wyrażenie Dzieci.2 oznaczałoby wiązanie do identyfikatora i3 (wartości "Adam"). W systemie Loqis nazwa może być obliczona przez zapytanie, zatem możliwe jest użycie takich wyrażeń, jak np.

P6.11.

Dzieci.[x+1]

które przy wartości obiektu (zmiennej) x równej 2 zwróci i4, czyli identyfikator obiektu z wartością "Anna". Takie tablice mogą być dynamiczne, tj. nie ograniczone od góry. Podane pomysły mogą być użyte do realizacji pojęcia sekwencji bez wykraczania poza założenia modeli M0 - M3.

Sekwencje można również uwzględnić poprzez nowy model składu obiektów zawierający obiekty dwóch rodzajów: obiekty, w których kolejność podobiektów nie ma znaczenia, oraz obiekty, w których kolejność podobiektów ma znaczenie. Wyróżnikiem rodzaju obiektu byłaby odpowiednia flaga zawieszona nad obiektem. W zależności od tej flagi, możliwe byłoby użycie operatorów, np. operator "daj następny podobiekt" zastosowany w stosunku do obiektu z flagą "jestem uporządkowany" zawsze zwróciłby tę samą kolejność podobiektów, co umożliwiłoby wykorzystania porządku do przechowywania trwałej informacji. Tego rodzaju konwencja rozwiązałaby kwestię typów, w których kolejność elementów ma znaczenie. Technologie oparte na XML, np. model DOM, przyjmują mniej lub bardziej explicite, że kolejność podobiektów w ramach obiektu ma znaczenie, tj. mając dany podobiekt można przejść do następnego podobiektu. Nie jest natomiast jasne, czy ten porządek jest trwały, tj. może służyć do reprezentacji informacji. W SQL przy przetwarzaniu poprzez kursory przyjmuje się podobne założenie, ale zwykle systemy relacyjne nie gwarantują trwałości porządku (czyli nie może być użyty do przechowywania informacji).


3.14. Model M1 - klasy i dziedziczenie

Wprowadzony wyżej model składu obiektów uzupełnimy w modelu M1 o pojęcia klasy i dziedziczenia. Na użytek definicji semantyki języków zapytań przyjmiemy tutaj model obiektowy w wersji prototypów. Koncepcja ta zakłada, że:

Z formalnego punktu widzenia klasa jest obiektem podobnym do wprowadzonych poprzednio obiektów. Jest przechowywana w składzie obiektów.

Obiekty będące klasami będą wyróżnione jako te, które przechowują inwarianty innych obiektów. Ta rola klas będzie miała wpływ na definiowaną przez nas semantykę języków zapytań. Istotna zmiana w modelu formalnym M1 polega na wprowadzeniu specjalnych powiązań którego celem jest odwzorowanie relacji dziedziczenia. Te powiązania uzupełniają nasz model składu. Powiązania są modelowane jako dwie relacje binarne zdefiniowane na obiektach znajdujących się w składzie.

W modelu M1 skład jest zdefiniowany jako czwórka <S, R, KK, OK>, gdzie:

  • S jest zbiorem obiektów (rozszerzonym o klasy),
  • R jest zbiorem identyfikatorów obiektów będących "wejściem" do nawigacji w obiektowej strukturze danych,
  • relacja KK Í I ´ I wyznacza związek dziedziczenia pomiędzy klasami,
  • relacja OK Í I ´ I wyznacza przynależność obiektów do klas

28
Rys.28. Model M1 - skład obiektów ze związkami dziedziczenia

Dla każdej pary <i1, i2> Î KK, i1 oznacza identyfikator klasy dziedziczącej, zaś i2 oznacza identyfikator klasy, z której się dziedziczy. Na rysunkach parę identyfikatorów należącą do KK będziemy zaznaczać w postaci strzałki z białym grotem prowadzącej od klasy dziedziczącej do klasy dziedziczonej. Dla każdej pary <i1, i2> Î OK, i1 oznacza identyfikator obiektu należącego do klasy identyfikowanej przez i2. Na rysunkach parę identyfikatorów należącą do OK będziemy zaznaczać w postaci grubej strzałki z czarnym grotem prowadzącej od obiektu dziedziczącego do jego klasy.

Rys.28 pokazuje formalny skład obiektów ze związkami dziedziczenia, zaś Rys.29 pokazuje tę samą sytuację na bardziej poglądowym modelu.

Poza wprowadzeniem związków dziedziczenia w składzie obiektów na razie nie będziemy wypowiadali się o naturze dziedziczenia i co konkretnie oznacza to pojęcie. Dla potrzeb definicji składu obiektów jest to niepotrzebne. Precyzyjne określenie co to znaczy "dziedziczy", może nastąpić dopiero wtedy, gdy zajmiemy się regułami zakresu i wiązania, co nastąpi w kolejnych fragmentach tej książki. Sytuacja przedstawiona na Rys.28 i Rys.29 przedstawia bardzo popularną wizję modelu obiektowego i można ją zastosować do większości znanych języków i propozycji, włączając C++, Smalltalk, Java, standard CORBA, standard ODMG i inne.

29
Rys.29. Model M1 - poglądowy obraz składu obiektów z Rys.28.

Dla naszych rozważań nie będzie istotne, czy identyfikatory klas znajdują się czy nie znajdują się wśród identyfikatorów startowych. Na ogół będziemy przyjmować, że klasy nie będą mogły być bezpośrednio przeszukiwane przez język zapytań, wobec czego ich identyfikatory nie są dostępne jako punkty startowe w powyższym składzie obiektów. Identyfikatory te mogą być jednak wyróżnione i istotne, o ile będziemy rozważać inne operacje na klasach, np. modyfikację lub usunięcie klasy. W naszym modelu jest również nieistotne, czy wszystkie klasy znajdują się na górnym poziomie hierarchii obiektów. Model M1 obejmuje również przypadek, kiedy klasa jest podobiektem innego obiektu (zwanego wtedy modułem) oraz dowolne inne tego typu przypadki.

Może powstać również pytanie, czy elementy relacji KKOK mają podlegać zasadzie wewnętrznej identyfikacji, tj. czy są także obiektami mającymi swoje nazwy i identyfikatory. Taka potrzeba może wyniknąć wtedy, gdy zależy nam na aktualizacji (tworzeniu, wstawianiu, usuwaniu) powiązań pomiędzy obiektami i/lub klasami w definiowanym przez nas języku zapytań/programowania. Np. w takim języku chcielibyśmy zdefiniować operację podwiązania obiektu do innej klasy, odłączenia obiektu od pewnej klasy, podłączenia klasy do innej nadklasy. Takie operacje są rozważane przez różnych autorów, m.in. w związku z tematem określanym jako ewolucja schematu (schema evolution).

W wielu językach i systemach obiektowych zakłada się, że zarówno klasy, jak i związki dziedziczenia są drugiej kategorii programistycznej, czyli istnieją wyłącznie w tekście programu. Nasz model explicite zakłada, że klasy i związki dziedziczenia są pierwszej kategorii programistycznej. Można jednak przyjąć, że przesunięcie do drugiej kategorii jest zabiegiem optymalizacyjnym, który umożliwia pewne operacje w czasie kompilacji oraz efektywniejszą reprezentację danych i kodu. Zabieg ten uniemożliwia także pewne operacje (np. aktualizacje) podczas czasu wykonania. Z punktu widzenia semantyki języka zapytań przesunięcie klas do drugiej kategorii programistycznej oznacza, że wiązanie pewnych nazw może i powinno odbywać się w czasie kompilacji. Przyjęty przez nas poziom abstrakcji w opisie semantyki nie wyróżnia jednak fazy kompilacji. Inaczej mówiąc, jakkolwiek w naszym modelu klasy mają pierwszą kategorię obywatelstwa, przesunięcie ich do drugiej kategorii nie będzie miało istotnego wpływu na semantykę, lecz raczej na możliwość bądź brak możliwości definiowania pewnych operacji na obiektach i/lub klasach.

Jak już dyskutowaliśmy, nasz model jest ortogonalny w stosunku do typów. Typ może być inwariantem wprowadzanej przez nas klasy, czyli jest jej składową na takiej samej zasadzie jak np. przedstawione na Rys.28 metody. Ta składowa nie jest (z reguły) wykorzystywana przez język zapytań, ale raczej przez analizator poprawności typologicznej obiektów, zapytań i programów. Typ może być również wykorzystany jako wzorzec (lub szablon) przez pewną metodę tworzenia nowego obiektu. W przedstawionej dalej semantyce będziemy abstrahować od tego rodzaju funkcjonalności. Co za tym idzie, z naszego punktu widzenia nie jest istotne, czy typ jest pierwszej czy też drugiej kategorii programistycznej.

Model M1 obejmuje również wielokrotne dziedziczenie. Definiując relacje KK i OK nie ograniczaliśmy możliwości istnienia w niej dwóch lub więcej par, gdzie pierwszy element (identyfikator obiektu dziedziczącego) jest identyczny, zaś drugi element (identyfikator obiektu, z którego się dziedziczy) jest różny.

Model M1 zawiera w szczególny sposób informację niezbędną do podtrzymania zasady zamienialności (LSP, substitutability). Zasada ta mówi, że każdy obiekt klasy podrzędnej jest jednocześnie obiektem klasy nadrzędnej; np. każdy obiekt Prac jest jednocześnie obiektem Osoba. Wynika to pośrednio ze związków między klasami, w związku z czym będziemy wykorzystywać te związki dla bardzo szczególnego procesu wiązania nazw. Mianowicie, jeżeli w danym zapytaniu jest wiązana nazwa Osoba, to wynikiem wiązania są nie tylko referencje do obiektów Osoba, ale również referencje do obiektów Prac. Taka reguła wiązania będzie wykorzystywać fakt, że KlasaOsoba zawiera inwariant ustalający nazwy podwiązanych obiektów na Osoba, klasa KlasaPrac zawiera inwariant ustalający nazwy podwiązanych obiektów na Prac, zaś pomiędzy tymi klasami istnieje związek dziedziczenia. Ten mechanizm zostanie objaśniony dalej.


3.15. Model M2 - modelowanie dynamicznych ról

Aby uniknąć problemu z realizacją zasady zamienialności zasygnalizowanego na końcu poprzedniej sekcji oraz zdefiniować dalsze własności, wprowadzimy model składu obiektów M2. Celem tego modelu jest wprowadzenie koncepcji dynamicznych ról obiektów.

Model M2 jest uporządkowaną piątką <S, R, KK, OK, OO>, gdzie nowa relacja OO Í I ´ I wyraża dziedziczenie pomiędzy obiektami.

Relacja OO posiada podwójną funkcję. Z jednej strony, pozwala obiektom dziedziczyć z innych obiektów na takiej samej zasadzie jak obiekty dziedziczą z klas. Obiekty dziedziczące z obiektu A będziemy nazywać rolami obiektu A. Możliwe jest, że sam obiekt A jest rolą, w szczególności obiekt Osoba; w tej sytuacji obiekty dziedziczące z A będziemy nazywać podrolami. Relacja OO ustala także semantykę manipulacji obiektami z dynamicznymi rolami. W szczególności, usunięcie obiektu będzie powodować usunięcie wszystkich jego ról.

Sytuacja z dynamicznymi rolami została przedstawiona na Rys.30 i Rys.31. Obiekty PracStudent nie mają teraz atrybutów NazwiskoRokUr, ale dziedziczą je z odpowiednich obiektów Osoba poprzez dodatkowy związek dziedziczenia pomiędzy obiektami. Model ten bezpośrednio nawiązuje do koncepcji ról omówionej poprzednio. Model ten jest wolny od pewnych anomalii typologicznych (których tu nie będziemy dyskutować) i formalnie bardziej "czysty" w stosunku do modelu M1.

30
Rys.30. Model M2 - modelowanie dynamicznych ról

Intencją wprowadzenia modelu M2 jest umożliwienie dziedziczenia atrybutów (wraz z wartościami) z obiektów przez inne obiekty. Na Rys.30 i Rys.31 obiekty PracStudent dziedziczą z obiektu Osoba atrybuty NazwiskoRokUr. W ten sposób obiekty PracStudent mogą być traktowane podobnie jak to zostało przedstawione w modelu M1. Jednocześnie, dziedziczą one swoje inwarianty z klas odpowiednio KlasaPracownikKlasaStudent. Dalsze intencje wprowadzenia tego modelu są następujące:

  • Zasada zamienialności ulega modyfikacji, gdyż np. obiekt Prac ma explicite swój fragment nazwany Osoba, wobec tego użycie w zapytaniu nazwy Osoba automatycznie zwiąże wszystkich pracowników (studentów itd.).

  • Obiekt (traktowany jako byt świata zewnętrznego) nie jest identyfikowany przez jedną nazwę, a przez nazwy wszystkich jego ról. Obiekty świata zewnętrznego zostały na Rys.31 ograniczone prostokątem z przerywaną linią. Obiekt Kowalskiego może być identyfikowany przez nazwy Osoba, Prac i Student, ale w każdej z tych ról posiada nieco inną semantykę. Np. jeżeli wchodzimy do tego obiektu przez nazwę Student, to niewidoczne są jego atrybuty ZarobekPracujeW. Zatem podana koncepcja realizuje konsekwentnie pomysł wielu interfejsów do tego samego obiektu.

  • Koncepcja ta w semantycznie klarowny sposób uwzględnia wielokrotne dziedziczenie, usuwając anomalie wielokrotnego dziedziczenia. W szczególności, nie powstanie konflikt, jeżeli obiekty PracStudent, będące specjalizacjami tego samego obiektu Osoba, będą miały tak samo nazwane atrybuty lub tak samo nazwane metody.

  • Intencją tego modelu jest modelowanie integralności obiektów poprzez kaskadowe usuwanie (kopiowanie, przesuwanie). Mianowicie, jeżeli będziemy usuwać obiekt Osoba, to zadbamy o to, aby usunąć również wszystkie role PracStudent związane z tym obiektem relacją OO. (Podobnie z kopiowaniem i przesuwaniem).

  • W podanym modelu niektóre związki dziedziczenia pomiędzy klasami (relacja KK) są zbędne, gdyż np. obiekty Prac dziedziczą z klasy KlasaOsoba pośrednio poprzez obiekt Osoba. Z tego powodu na Rys.30 relacja KK jest zbiorem pustym, ale tylko z punktu widzenia składu obiektów. Istnienie takiej relacji jest istotne dla modelowania pojęciowego i powinna ona być uwzględniona w języku schematu. Z tego względu na Rys.31 uwzględniliśmy tę relację w postaci strzałek dziedziczenia narysowanych przerywaną linią. Strzałki te nie mają znaczenia dla definiowanej przez nas semantyki języka zapytań.


3.16. Model M3 - hermetyzacja i ukrywanie informacji

Model M3 uwzględniający hermetyzację możemy zbudować zarówno na gruncie modelu M1, jak i modelu M2, ponieważ cecha hermetyzacji jest ortogonalna w stosunku do wprowadzonych wcześniej własności. W związku z tym przyjmiemy oznaczenia M3.1, dla oznaczenia modelu M1 rozszerzonego o hermetyzację oraz M3.2 dla podobnego rozszerzenia modelu M2.

31
Rys.31. Poglądowy obraz struktury z Rys.30

Idea hermetyzacji polega na tym, aby w określonych sytuacjach zabronić dostępu do pewnych własności obiektów, określanych jako "prywatne". Nie oznacza to oczywiście, że takie własności mają być w ogóle niedostępne; chodzi o to, aby były dostępne z "wnętrza" obiektu, zaś niedostępne z jego "zewnętrza". Tego rodzaju reguły dostępu (reguły zakresu dla nazw) będą wymuszone poprzez stosową semantykę języka zapytań. Na obecnym etapie definiowania modeli składu obiektu chodzi nam wyłącznie o to, aby istniała możliwość podziału obiektów i ich własności na "publiczne" i "prywatne".

Model M3 uzupełnia model M1 lub M2 w taki sposób, że każda klasa zostaje wyposażona w dodatkowy inwariant zwany listą eksportową. Jest ona zbiorem nazw własności tej klasy (w szczególności metod) oraz nazw własności jej obiektów (w szczególności atrybutów), które będą widoczne z zewnątrz.

32
Rys.32. Model M3.1 - uzupełnienie modelu M1 o listy eksportowe

Lista eksportowa będzie użyta w procesie ewaluacji zapytań jako dodatkowy środek kontroli zakresu nazw.

Rys.32 przedstawia przykład modelu M3 rozszerzającego model M1. Jak widać, przyjęliśmy tam, że każda klasa zawiera jako inwariant listę eksportową zawierającą nazwy własności publicznych. Przyjęliśmy także, że lista eksportowa jest dziedziczona (np. klasa Prac dziedziczy listę eksportową klasy Osoba), przy czym listy dziedziczone są sumowane z własną listą eksportową danej klasy.


3.17. Schemat bazy danych dla modeli składu obiektów

Schemat bazy danych (database schema) jest obrazem zawartości bazy danych, wyrażonym w sformalizowanym języku. Jest niezbędny dla użytkowników i programistów do zrozumienia, co baza danych zawiera i jak dane są zorganizowane. Schemat bazy danych jest również wykorzystywany przez SZBD dla właściwej organizacji danych, kontroli typów danych oraz wymuszenia niektórych ograniczeń dotyczących danych. Schemat bazy danych jest wyrażany w pewnym języku zwanym także językiem opisu danych (data description language, DDL). Przykładem takiego języka jest ODL według standardu ODMG. Schematy są również wyrażane w postaci graficznej; przykładem jest notacja UML.

Schemat ogranicza zawartość bazy danych. Formalnie, semantyką schematu bazy danych jest zbiór wszystkich jej dopuszczalnych stanów.

W naszym przypadku semantyką schematu byłby (zwykle nieskończony) zbiór dopuszczalnych zawartości składu obiektów. Dla celów zaprezentowania przykładów modeli danych konieczne byłoby opracowanie pewnego języka schematów. Np. na Rys.23 zaprezentowaliśmy schemat klas opisujący przykład modelu M0 w notacji zbliżonej do UML. Jak się okazało, dla potrzeb modeli M0 - M3 istniejące języki i notacje takie jak ODL lub UML okazały się niewystarczające od strony koncepcyjnej i mało precyzyjne. Np. ODL nie ma możliwości zapisu list eksportowych, zaś ODL jak i UML nie mają możliwości reprezentowania dynamicznych ról obiektów. Dla omówionych modeli składu konieczne byłoby opracowanie języka umożliwiającego zapis schematu. Jest to jednak duże zadanie, przekraczające ramy tej książki. Z tego powodu w dalszej części wykładu będziemy stosować notację ad hoc (zwykle wzorowaną na UML, jak na Rys.23) popartą objaśnieniem i przykładami.

Copyrights © 2006 PJWSTK
Materiały zostały opracowane w PJWSTK w projekcie współfinansowanym ze środków EFS.