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)
  Wstęp
  1. Klasy
  2. Polimorfizm
  3. Role
  4. Kolekcje
  5. Trwałość
  6. Moduły
  Podsumowanie
  Zadania
V.
Podstawy semantyczne języków zapytań
VI.
Modele składu obiektów
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. Role

W popularnym stereotypie obiektowości mówi się np. że student jest osobą. Z punktu widzenia modelowania pojęciowego jest to fraza nieprecyzyjna, ponieważ nie uwzględnia dynamizmu związanego z cyklem życia obiektu. Bardziej odpowiednim stwierdzeniem jest, że osoba staje się studentem. Ta parafraza ma na celu podkreślenie faktu, że każdy obiekt w czasie swojego życia może nabywać i tracić wiele ról lub specjalizacji, nie zmieniając swojej tożsamości. Role te zmieniają się dynamicznie. Osoba może jednocześnie być studentem, pracownikiem, pacjentem, członkiem klubu, podatnikiem, właścicielem psa, Rys.20. Role pojawiają się w trakcie życia danego obiektu, mogą istnieć równolegle oraz mogą zakończyć się.

Uwzględnienie takiej sytuacji w klasycznym modelowaniu pojęciowym za pomocą klas i wielodziedziczenia prowadzi do eksplozji liczby klas, zaś dynamizm zmian specjalizacji pewnego obiektu nie może być w tym modelu odwzorowany.

Koncepcja ról zakłada, że z obiektem można związać podobiekty, które odnoszą się do jednej z ról, aktualnie przypisanych do obiektu. Te podobiekty-role nie mogą istnieć bez obiektu zasadniczego (w danym przypadku, obiektu OSOBA). Usunięcie obiektu powoduje likwidację wszystkich jego ról. Role mogą występować jednocześnie, równolegle i są od siebie niezależne.

Tak rozumiane dynamiczne role obiektów stały sie przedmiotem wielu prac, niekiedy pod innymi nazwami oraz z różną semantyką, patrz np. [Alba93, Alba95, Bert95, Fish97, Gott96, Harri93, Rich91, Stei00, Stei01, Wong99]. Role są również obecne w standardzie SQL-99, pod nazwą podtablica (sub-table). W dalszych rozdziałach książki koncepcja ról zostanie sformalizowana w postaci modelu składu obiektów M2. Jak dotąd, tylko podejście stosowe umożliwia zbudowanie języka zapytań dla modelu obiektowego z dynamicznymi rolami. Niżej przedstawimy naszą wizję koncepcji dynamicznych ról obiektów.

21
Rys.21. Role, które mogą być przypisane do obiektu OSOBA

Każda rola może mieć własne (dodatkowe) atrybuty. Dwie role mogą mieć jednocześnie atrybuty o tych samych nazwach, które nie mają ze sobą związku i co innego znaczą. Przykładowo, osoba może pełnić rolę pracownika instytutu naukowego, i w ramach tej roli posiadać atrybut Zarobek oraz może jednocześnie pełnić rolę pracownika firmy usługowej, i w ramach tej roli również posiadać atrybut Zarobek; te dwa atrybuty występują jednocześnie, mają tę samą nazwę, ale nie mają ze sobą nic wspólnego.

Rola importuje wszystkie atrybuty wraz z ich wartościami z obiektu będącego jej podmiotem. Każda z ról może należeć do własnej klasy. Sytuacja ta jest zilustrowana na Rys.21. Obiekt OSOBA może występować w rolach PRACOWNIKSTUDENT (które także są obiektami), jednocześnie i niezależnie. Może także nie występować w żadnej z tych ról. Zarówno OSOBA, jak i role PRACOWNIK oraz STUDENT posiadają dostęp do pewnych informacji zgrupowanych w ramach odpowiednich klas. Każda rola importuje atrybuty wraz z ich wartościami od obiektu będącego jej podmiotem; ten import jest zaznaczony strzałką o podwójnej linii.

Koncepcja ról nie sprowadza się tylko do zagnieżdżania obiektów. Oprócz podstawowej różnicy, polegającej na tym, że role mogą dynamicznie pojawiać się i znikać, istnieje szereg aspektów semantycznych, które omówimy niżej.


3.1. Wiązanie

Obiekt z rolami posiada nie jedną, a kilka nazw (np. OSOBA, PRACOWNIK, STUDENT). Wszystkie te nazwy widoczne są na tym samym poziomie. Użycie każdej z nich powoduje nieco inne wiązanie, w zależności od użytej nazwy. Przykładowo (Rys.21), zapytanie

P4.2.

OSOBA where Nazwisko = "Nowak"

zwróci identyfikator obiektu Nowaka, natomiast zapytania

P4.3.

PRACOWNIK where Nazwisko = "Nowak"


P4.4.

STUDENT where Nazwisko = "Nowak"

zwrócą odpowiednio identyfikatory (niesamodzielnych) obiektów-ról obiektu Nowaka, przechowujących odpowiednio jego role PRACOWNIKSTUDENT.


Możliwie jest utworzenie roli nie posiadającej żadnego nowego atrybutu, a wyłącznie nazwę. Taka nazwa staje się synonimem nazwy obiektu, ale ze względu na różnice w wiązaniu (posiadanie własnego identyfikatora) ten synonim jest dość specyficzny, gdyż umożliwia własne wiązania. Przykładowo, do obiektów OSOBA możemy wstawić "puste" role KRWIODAWCA, co umożliwia z jednej strony szybką selekcję osób, które są krwiodawcami, np.

P4.5.

(KRWIODAWCA where RokUr < 1975). Nazwisko

zaś z drugiej strony można wiązać takie role związkami asocjacyjnymi (np. powiązać z ośrodkiem krwiodawstwa) i podłączać do własnych klas. W ten sposób koncepcja ról ma pewne przecięcie koncepcyjne z pojęciem perspektywy (view). W podanym przykładzie KRWIODAWCA może być uważany za pewną szczególną perspektywę obiektów OSOBA.


3.2. Powiązania pomiędzy obiektami i rolami

W koncepcji ról powiązania mogą łączyć nie tylko obiekty, lecz także obiekty i role. Możliwe jest także ustalenie powiązań pomiędzy rolami. Przykładowo, powiązanie pracuje_w pomiędzy obiektami OSOBAFIRMA łączy w istocie obiekt FIRMA z rolą PRACOWNIK, Rys.21. Podobnie powiązania studiuje_na łączą role STUDENT z obiektami UCZELNIA. Jeżeli takie powiązanie jest implementowane jako pointer, to prowadzi on do roli PRACOWNIK, a nie do całego obiektu OSOBA. To wymaganie oznacza, że identyfikator roli musi być różny od identyfikatora obiektu. Rys.21 pokazuje także powiązanie jest_klientem pomiędzy obiektem OSOBA i obiektem FIRMA. To powiązanie jest niezależne od jakiejkolwiek roli, którą aktualnie posiada obiekt OSOBA.

Umożliwienie powiązań prowadzących do ról, a nie do całych obiektów, jest nową sytuacją dla dotychczas znanych obiektowych metodyk analizy i projektowania.

Dotychczas (np. w OMT i UML) tego rodzaju sytuacje były dość sztucznie podciągane pod pojęcie określane jako "agregacja". W istocie "agregacja" dotyczy sytuacji "całość-część", np. samochód-silnik, i w zasadzie nie stosuje się pojęciowo do koncepcji ról: student nie jest "częścią" osoby na takiej samej zasadzie, jak silnik jest częścią samochodu. Dzięki rolom diagramy klas (obiektów) mogą znacznie bardziej precyzyjnie i intuicyjnie odwzorowywać analizowaną lub projektowaną semantykę powiązań pomiędzy obiektami. Z drugiej strony, pewne obawy może budzić wzrost koncepcyjnej złożoności diagramów, który może utrudniać ich percepcję.


3.3 Semantyka usuwania

Usunięcie obiektu automatycznie usuwa wszystkie jego role, ale usunięcie roli nie oznacza oczywiście usunięcia obiektu. Np. instrukcja

P4.6.

delete OSOBA where Nazwisko = "Kowalska"

usuwa z bazy danych cały obiekt opisujący Kowalską, wraz z wszystkimi jego rolami; natomiast instrukcja

P4.7.

delete PRACOWNIK where Nazwisko = "Kowalska"

usuwa z wnętrza tego obiektu rolę PRACOWNIK. Role realizują więc semantykę kaskadowego usuwania znaną z wielu systemów baz danych.


3.4. Reguły zakresu dla wiązania atrybutów

Występuje tu istotna różnica w porównaniu z podobiektami. Jeżeli wiązanie dotyczy obiektu OSOBA, wówczas nie jest widoczna żadna z jego ról. Uwidocznienie ról obiektu OSOBA wymaga dodatkowych opcji (koercji), które omówimy nieco dalej. Natomiast jeżeli wiązanie dotyczy roli, wówczas są widoczne atrybuty tej roli oraz atrybuty obiektu; nie są oczywiście widoczne atrybuty innych ról. Przykładowo, zapytanie

P4.8.

OSOBA where Zarobek > 2000

nie jest poprawne, gdyż wiązanie nastąpiło do obiektu OSOBA, zaś atrybut Zarobek jest fragmentem roli tego obiektu. Również nie jest poprawne zapytanie

P4.9.

STUDENT where Zarobek > 2000

ponieważ atrybut Zarobek nie jest atrybutem roli STUDENT ani też obiektu OSOBA. Natomiast zapytania

P4.10.

STUDENT where Nazwisko = "Nowak"


P4.11.

STUDENT where Semestr = 7

są poprawne. Rola może mieć własny atrybut o takiej samej nazwie jak atrybut obiektu. W takim przypadku wiązanie do roli oznacza, że oryginalny atrybut obiektu zostaje przesłonięty; natomiast jest on widoczny w przypadku wiązania do obiektu.


3.5. Reguły zakresu dla metod

Są one bardzo podobne do reguł wiązania atrybutów. Przykładowo, zapytanie

P4.12.

(OSOBA where Nazwisko = "Nowak").ObliczŚredniąOcen()

jest niepoprawne, natomiast zapytanie

P4.13.

(STUDENT where Nazwisko = "Nowak").Wiek()

jest poprawne.

Należy jeszcze zwrócić uwagę na istotny szczegół semantyczny. Mimo że komunikat Wiek() został wysłany do roli STUDENT obiektu OSOBA, metoda Wiek działa na atrybutach obiektu OSOBA i nie widzi atrybutów roli STUDENT; natomiast metoda ObliczŚredniąOcen widzi atrybuty roli STUDENT w pierwszej kolejności, zaś następnie atrybuty obiektu OSOBA. Takie ustawienie reguł zakresu jest konsekwencją hermetyzacji. Twórca klasy OSOBA pisząc metody dla tej klasy nie musi uwzględniać jakichkolwiek atrybutów ról, które (zwykle) pojawiają się później, po zaimplementowaniu klasy OSOBA. Ta sama reguła obowiązuje dla widoczności metod: metody takie jak Wiek "nie widzą" metod takich jak ObliczŚredniąOcen, ZmieńZarobek itd.; natomiast te metody "widzą" metodę Wiek (czyli ta metoda może być wywołana wewnątrz ich ciała).


3.6 Koercje dla identyfikatorów ról

W niektórych przypadkach może okazać się przydatna możliwość zamiany identyfikatora roli na identyfikator macierzystego obiektu lub innej jego roli, a także zamiana identyfikatora obiektu na identyfikator pewnej jego roli. Jeżeli obiekt nie posiada danej roli, wówczas wynik jest pusty (nil). Przykładowo, używając notacji określenia typu (cast) w ten sposób można byłoby zadawać następujące zapytania:

P4.14.

Podaj pracowników, którzy są jednocześnie studentami:

(PRACOWNIK) STUDENT

Wartościowanie zapytania STUDENT zwraca listę identyfikatorów ról STUDENT, w danym przypadku, studentów NowakNowacki. Operator koercji (PRACOWNIK)zastosowany do obu z nich zwróci w pierwszym przypadku identyfikator roli PRACOWNIK obiektu Nowaka, zaś w drugim przypadku zwróci nil (czyli nic).

P4.15.

Podaj osoby, których zarobek netto przekracza 3000:

(OSOBA) (PRACOWNIK where ZarobekNetto() > 3000)

Analogicznie, można byłoby wprowadzić specjalne wyrażenia boolowskie testujące obecność określonej roli u określonego obiektu.


3.7. Role: ogólne obserwacje

Koncepcja ról łączy mechanizmy udostępniania informacji poprzez klasy i poprzez prototypy (obiekty pełniące rolę klas), ale jednocześnie wprowadza mocne środki w zakresie modelowania pojęciowego. Jest to istotna różnica w stosunku do pojęcia prototypu, który w gruncie rzeczy można uważać za konstrukcję modelu obiektowego odnoszącą się nie do modelowania pojęciowego, lecz do szczególnej techniki realizacyjnej. Połączenie pomiędzy klasami np. STUDENTOSOBA nie występuje tu explicite. Nie zachodzi bowiem potrzeba łączenia klasy roli i klas obiektów, w których ta rola może wystąpić. Można sobie łatwo wyobrazić, że ta sama klasa roli może być przypisana do obiektów różnych klas; przykładowo, rola PACJENT może dotyczyć obiektu klasy OSOBA oraz klasy ULUBIENIEC. Role mogą być zagnieżdżane; przykładowo, PACJENT może być podrolą roli PRACOWNIK.

Brak pojęcia roli w modelach i systemach obiektowych powoduje ograniczenia i konflikty, które następnie zamieniają się w dość jałowe dyskusje dotyczące metod ich obejścia, lub w krytykę koncepcji wielodziedziczenia, której konsekwencją jest usunięcie tej własności z obecnie lansowanych języków programowania (Java, Modula-3). Jest to klasyczna sytuacja, kiedy ułomna, zbytnio uproszczona realizacja pewnej koncepcji powoduje ataki na samą koncepcję. Podstawowe problemy wielo-dziedziczenia biorą się stąd, że w jednym obiekcie lub jednej klasie mogą znaleźć się niekompatybilne ze sobą lub konfliktowe atrybuty. Tej wady nie jest w stanie w pełni usunąć ani koncepcja zmiany nazw przy dziedziczeniu, ani też koncepcja wielu interfejsów do jednego obiektu; są to tylko półśrodki pozwalające w wielu sytuacjach na obejście problemu. Natomiast koncepcja ról wprowadza klarowność w tym zakresie, wskazując zarówno na przyczynę problemów, jak i sposób ich rozwiązania.

Jak dotąd, role nie są implementowane i praktycznie nie występują w metodykach analizy i projektowania. Zamiast nich autorzy tych metodyk posługują się zbytnio uogólnionym terminem agregacja lub semantycznie mętnym terminem delegacja. Role, wraz z istotnymi możliwościami w zakresie modelowania pojęciowego, powodują pewien wzrost złożoności modelu obiektowego i w konsekwencji wzrost stopnia skomplikowania systemu. Dotyczy to w szczególności systemów mocnej kontroli typu, które, jak się wydaje, są niezbyt przygotowane do realizacji tej cechy. Role można uważać za uogólnienie wariantów (unii), które nawet w prostym rozwiązaniu sprawiają trudności dla mocnej kontroli typów. Chyba jedynym systemem, który zaimplementował koncepcję ról, jest Fibonacci (w wersji mocno ograniczonej w stosunku do przedstawionej wyżej koncepcji).

Jak łatwo zauważyć, role są koncepcją uniwersalną i obejmują nie tylko wielodziedziczenie, ale także zwyczajne dziedziczenie. Przykładowo, obiekt STUDENT można uważać nie za specjalizację obiektu OSOBA, lecz za obiekt OSOBA posiadający rolę STUDENT. W ten sposób do takiego obiektu można odwołać się na dwa sposoby: (1) używając nazwy OSOBA mamy dostęp wyłącznie do tych atrybutów, które cechują studenta jako osobę; (2) używając nazwy STUDENT mamy dostęp do wszystkich atrybutów. Tę koncepcję można uogólnić na dowolnie rozbudowaną hierarchię klas.

Takie potraktowanie dziedziczenia praktycznie eliminuje potrzebę wprowadzania zasady zamienialności (substitutability) (którą zdefiniujemy nieco dalej). Co za tym idzie, eliminowane są anomalie semantyczne implikowane przez tę zasadę (np. anomalie operacji podstawienia), jak również wprowadza się znacznie większą klarowność systemu typów. Z drugiej strony, pewną wadą takiego podejścia jest rezygnacja z polimorfizmu metod (ale nie z przesłaniania). Np. metoda dochody dla studenta przesłania metodę dochody dla osoby, o ile używamy jej w kontekście STUDENT.dochody. Jeżeli natomiast napiszemy OSOBA.dochody, wówczas wywołana zostanie metoda dochody dla OSOBY, nawet jeżeli ta osoba jest aktualnie studentem. Jest dość trudno przewidzieć, czy ta zmiana semantyczna i pragmatyczna prowadzi do istotnych wad i czy można ich uniknąć przez poprawki do nakreślonej wyżej koncepcji.

Temat ról jest przedmiotem kilku tzw. wzorców projektowych (design patterns) (np. decorator [Gamm94, Fowl00]), w których obiekty z rolami odwzorowuje się na pewne złożone struktury danych, które z kolei są obsługiwane przez wzorcowe fragmenty oprogramowania w językach C++ lub Java. Takie potraktowanie ról ma pewne zalety, mianowicie, dostosowuje do niej istniejące metodyki, notacje (OMT, UML) i języki programowania. Z drugiej strony, są to próby stworzenia kolejnych protez zamiast rozwiązania problemu. Istotą ról jest uproszczenie modelowania pojęciowego dla wielu sytuacji oraz wspomożenie go przez proste mechanizmy w języku zapytań i programowania. Autor książki jest zdania, że koncepcja opanowania problemu przez wzorce projektowe nie jest w stanie osiągnąć tych celów w satysfakcjonującym stopniu.

Temat dynamicznych ról był przedmiotem rozprawy doktorskiej [Jodl03a] oraz kilku związanych z nią artykułów [Jodl01, Jodl02a, Jodl02b, Jodl03b, Plod02, Subi03].

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