Transcript Document

Standardy w zakresie systemów rozproszonych
i baz danych
Wykład 10:
Wprowadzenie do OCL
(Object Constraint Language)
Piotr Habela
Kazimierz Subieta
Polsko-Japońska Wyższa Szkoła
Technik Komputerowych, Warszawa
P.Habela, K.Subieta. SSR, Wykład 10, Folia 1
maj 2009
Geneza OCL
• Oryginalnie powstał aby specyfikować ograniczenia (constraints)
na modelu klas UML.
• Posiada moc rachunku predykatów niższego (pierwszego) rzędu +
plus elementy teorii zbiorów.
– Jest to moc znacznie mniejsza od mocy języków programowania
• Składnia jest silnie wzorowana na postfiksowym zapisie
operatorów stosowanym w języku Smalltalk
–
–
–
–
–
–
–
Ta składnia jest udziwniona
Zmieszana z innymi konwencjami
Klasyczne języki stosują składnię infiksową: x+y oraz prefiksową: sin(x)
Paradygmat: Obiekt . metoda(parametry)
Lub: KolekcjaObiektow -> metodaNaKolekcji(parametry)
Niektórym ludziom składnia ta się podoba
Ale nie wszystkim…, trudno wyczuć na czym polega jej przewaga
P.Habela, K.Subieta. SSR, Wykład 10, Folia 2
maj 2009
Historia OCL
• Główni autorzy: Jos Warmer, Anneke Kleppe
• System formalny Syntropy powstały pod wpływem notacji Z
• Język do modelowania biznesu (Language for Business Modeling)
• Propozycja IBM dotycząca ObjecTime dla OMG
• Stał się częścią standardu UML
– Podstawowym językiem dla Query-View-Transformer (QVT), propozycji
nowego standardu OMG dla odwzorowania modeli
• Rozszerzony i formalnie zdefiniowany dla UML 2.0, UML 2.1
• Liczne sugestie, że może być także użyty jako język zapytań z mocą
taką samą lub większą niż SQL
– Sugestie są pobożnym życzeniem, gdyż moc SQL nie wynika z możliwości
algorytmicznych, lecz z wydajności zapewnianej przez optymalizator zapytań
– Kwestia optymalizacji zapytań nie jest tematem przy okazji OCL, zatem nie
jest to język zapytań o skuteczności podobnej do SQL
– VIDE zweryfikował te sugestie wprowadzając również optymalizator zapytań
P.Habela, K.Subieta. SSR, Wykład 10, Folia 3
maj 2009
Przykładowy model w UML
Butelka
objętość : Integer
zawartość : Integer
waga : Integer
średnica : Integer
średnicaSzyjki : Integer
Skrzynka
0..*
butelki
łącznaWaga() : Integer
objętość : Integer
0..1 maxWaga : Integer
skrzynka
maxŚrednica : Integer
łącznaWaga() : Integer
dodajButelkę(b : Butelka)
0..*
skrzynki
0..1
paleta
butelka 0..1
kapsel
0..1
Kapsel
wewnętrznaŚred : Integer
waga : Integer
znakWygranej : Boolean
P.Habela, K.Subieta. SSR, Wykład 10, Folia 4
0..*
/ kapsle
Paleta
objętość : Integer
maxWaga : Integer
łącznaWaga() : Integer
dodajSkrzynkę(c : Skrzynka)
maj 2009
Inwarianty na klasach
• context Skrzynka
– Liczba butelek nie może przewyższać objętości skrzynki
inv: butelki -> size() <= objętość
– Każda butelka musi zmieścić się w skrzynce
inv: butelki -> forAll(średnica < maxŚrednica)
– Inny zapis:
forAll b : Butelka from butelki
isTrue b.średnica < maxŚrednica
– Łączna waga skrzynki musi być mniejsza od maksymalnej
wagi
inv: łącznaWaga() <= maxWaga
P.Habela, K.Subieta. SSR, Wykład 10, Folia 5
maj 2009
Inwarianty na stanie
• context Butelka
state zamknięta inv: zawartość = objętość
otwarta
Butelka
objętość : Integer
zawartość : Integer
waga : Integer
średnica : Integer
średnicaSzyjki : Integer
zamknięta
P.Habela, K.Subieta. SSR, Wykład 10, Folia 6
łącznaWaga() : Integer
maj 2009
Paradygmat „Design by Contract”
• Oznacza specyfikację inwariantów modułów
oprogramowania poprzez
– warunki początkowe (preconditions)
– warunki końcowe (postconditions)
• Preconditions na operacjach
context Skrzynka:: dodajButelkę( b : Butelka )
– Musi być miejsce w skrzynce:
pre: butelki -> size() < objętość
– Maksymalna waga skrzynki nie może być przekroczona
pre: łącznaWaga() + b. łącznaWaga() <= maxWaga
– Tej butelki nie ma jeszcze w skrzynce
pre: not butelki -> includes( b )
• Postcondition
– ‘b’ jest dodane do kolekcji butelek w skrzynce
post: butelki = butelki@pre -> including(b)
P.Habela, K.Subieta. SSR, Wykład 10, Folia 7
maj 2009
Reguły biznesowe
• Musi być co najwyżej tyle wygranych znaków pod kapslami na
każdej palecie ile jest skrzynek na palecie.
context Paleta inv:
kapsle->select(znakWygranej = true)->size() <= skrzynka->size()
• Inny wariant:
context Paleta inv:
sizeOf select c : Kapsel from self.kapsle
where c. znakWygranej = true
<= sizeOf self.skrzynki
• Dla porównania, jak to można byłoby zapisać w SBQL:
forall Paleta
count(kapsle.Kapsel where znakWygranej) <= count(skrzynki)
– OCL: 24 leksemów, SBQL: 15 leksemów
– Dla reguł biznesowych wyspecyfikowanie samego ograniczenia to za
mało. Konieczne jest jeszcze określenie akcji, która będzie podjęta jeżeli
ograniczenie nie będzie spełnione.
P.Habela, K.Subieta. SSR, Wykład 10, Folia 8
maj 2009
Wartości początkowe i wyliczane
• Wartość początkowa
context Butelka::zawartość
init: 0
• Wartości wyliczane
– Kapsle dla palety:
context Paleta::kapsle
derive: skrzynki.butelki.kapsel->asSet()
• Należy podkreślić, że nazwa wartości wyliczanej może być różnie
traktowana:
– Jako makro-definicja (nazwa kapsle jest wszędzie zastępowana tekstowo
przez skrzynki.butelki.kapsel->asSet()
– Jako funkcja (nazwa kapsle jest nazwą procedury)
– Jako perspektywa (view) (nazwa kapsle oznacza obiekty wirtualne, które
można aktualizować).
• Niestety, twórcy OCL nie zadbali o to, aby dokładnie sprecyzować
semantykę tej konstrukcji.
P.Habela, K.Subieta. SSR, Wykład 10, Folia 9
maj 2009
Ciała metod
• Niezbyt skomplikowane metody mogą być wyspecyfikowane w
OCL
– Ograniczenie do metod zawierających pojedyncze zapytanie
– Operacja łącznaWaga dla butelki sumuje wagę butelki i wagę kapsla:
context Butelka:: łącznaWaga() : Integer
body: waga + kapsel.waga
– Operacja łącznaWaga dla skrzynki sumuje wagę skrzynki i sumaryczną
wagę łącznych wag butelek:
context Skrzynka:: łącznaWaga() : Integer
body: waga + butelki.łącznaWaga()->sum()
• OCL jest przeznaczony wyłącznie do zapytań, efekty uboczne są
zabronione (aktualizacje), nawet w wywoływanych metodach.
• Można mieć wątpliwości, czy tego rodzaju środek do
specyfikowania metod warto wprowadzać.
– Co jeżeli programista zechce poprawić taką metodę i nie wystarczą mu
dostarczone możliwości?
– Zrobienie języka zapytań o pełnej mocy algorytmicznej nie jest wielkim
wyczynem, zrobiono to dziesiątki razy, patrz np. SBQL
P.Habela, K.Subieta. SSR, Wykład 10, Folia 10
maj 2009
Inne zastosowania OCL (zdaniem autorów)
• Strażnicy (guards) – przeciwdziałanie niedozwolonym operacjom
• OCL jako język zapytań co najmniej tak mocny jak SQL (BoldSoft)
– Brak wzmianek o optymalizacji dla bardzo dużych kolekcji danych czyni
twierdzenie trochę przedwczesnym (VIDE to zmienia)
• Wyrażenia dla modelu UML
–
–
–
–
Specyfikacja docelowego obiektu w interakcji
Przepływ obiektów w diagramach aktywności
Parametry dla komunikatów i sygnałów
… etc. …
• Definicja transformacji modeli w paradygmacie MDA
– Propozycja standardu QVT (Query-View-Transformer)
– W tej roli wydaje się mieć zbyt małą siłę ekspresji i moc algorytmiczną
• Język definicji i ograniczeń dla metamodelu UML
– Np. ograniczenie, ze w ramach klasy każdy atrybut musi mieć unikalną nazwę
• Reguły dla profili UML
P.Habela, K.Subieta. SSR, Wykład 10, Folia 11
maj 2009
OCL 2.0 – cechy warte uwagi
• Pełny język zapytań dla UML
– Słowo „pełny” jest tu pozbawione znaczenia, bo są języki „pełniejsze”, np.
SBQL
• Mocna kontrola typologiczna wyrażeń OCL
– w odróżnieniu od SQL, OQL, XQuery, itd.
• Kompletny metamodel dla OCL
– W pełni zintegrowany z metamodelem dla UML
– Również myślenie życzeniowe: VIDE pokazało liczne sprzeczności
• Składnia konkretna ściśle odseparowana od składni abstrakcyjnej
– Umożliwia to alternatywne składnie włączając wizyjne
• Semantyka zdefiniowana matematycznie i w UML
– Twierdzenie mało wiarygodne, zważywszy na błędy w specyfikacji,
nieprecyzyjną składnię, brak reguł zakresu dla nazw, itp.
– UML nie ma formalnej semantyki, więc nie można określić precyzyjnej
semantyki w UML!
P.Habela, K.Subieta. SSR, Wykład 10, Folia 12
maj 2009
OCL w VIDE
• Nie jest implementowany jako język do specyfikacji ograniczeń,
ale jako język zapytań a la SQL, SBQL, OQL
• Kilka konstrukcji nie zostało zaimplementowanych z powodu ich
słabej przydatności lub efektywności (np. @pre)
• Staraliśmy się utrzymać oryginalną składnię konkretną, ale to nie
było łatwe ze względu na błędy w specyfikacji
• Staraliśmy się utrzymać oryginalną semantykę, ale to nie było do
końca możliwe ze względu na ograniczenie funkcjonalności i
słabej specyfikacji
– Np. dotyczy to zakresu dla nazw występujących w wyrażeniach OCL
– W istocie, zaimplementowana semantyka jest semantyką SBQL-a
• Jako język zapytań, OCL musi być wyposażony w optymalizator.
– Nie są znane metody optymalizacyjne dedykowane dla OCL
– Stąd zostały zastosowane metody dla SBQL, co oznaczało przystosowanie
semantyki OCL do semantyki SBQL
P.Habela, K.Subieta. SSR, Wykład 10, Folia 13
maj 2009
Składnia OCL
• Z uwagi na swój rodowód, OCL posiada specyficzną
składnię.
• Trzeba się z nią zapoznać, gdyż wszystkie wyrażenia w
VIDE, np.
–
–
–
–
–
–
wyrażenia określające prawą i lewą stronę przypisania,
kolekcję, po której chcemy iterować,
rezultat zwracany przez metodę,
parametry wołania metody,
dane przewidziane do usunięcia)
itd.
• będą specyfikowane w OCL: zarówno w edytorze
wizualnym jaki i tekstowym.
P.Habela, K.Subieta. SSR, Wykład 10, Folia 14
maj 2009
OCL: Wyrażenia proste
• W przeciwieństwie do SQL, nie mamy tutaj
obowiązkowego zestawu klauzul.
• Kompletne wyrażenia/zapytania OCL mogą być tak
proste jak:
1
2+2
not (true)
‘Hello’
employee
• Mamy do dyspozycji tradycyjną składnię dla operatorów
arytmetycznych (*,/,+,-) i logicznych (not, and, or).
P.Habela, K.Subieta. SSR, Wykład 10, Folia 15
maj 2009
Wyrażenia warunkowe
• Operator wyrażenia warunkowego jest jedynym
dostępnym w języku operatorem ternarnym.
• Jego odpowiednik w C i Javie posiada składnię
( …? ... : …)
• W OCL – taką, jak objaśnioną na przykładzie:
if (x > y) then x else y endif
• Powyższe przykładowe wyrażenie odpowiada
wykonaniu funkcji
max(x,y)
P.Habela, K.Subieta. SSR, Wykład 10, Folia 16
maj 2009
Konkatenacja
• Konkatenacja w OCL ma rozbudowaną, i przez to
nieco niewygodną składnię
• Stosujemy operację
concat(<łańcuch do dołączenia>)
• np.
‘Użytkownik ‘.concat(nazwaUzytk).
concat(‘ zarejestrował się ‘).concat(data)
P.Habela, K.Subieta. SSR, Wykład 10, Folia 17
maj 2009
Operacje iteracyjne
• Jako język zapytań, OCL poświęca dużo uwagi przetwarzaniu
kolekcji.
• Stąd dostępność typowych operatorów języka zapytań.
• W OCL zdefiniowano je jako operacje specjalne na typach
kolekcyjnych.
• Charakterystyczną ich cechą jest to, że wyrażenie po prawej
stronie jest liczone w kontekście (środowisku) określonym przez
aktualnie przetwarzany element rezultatu wyrażenia z lewej strony.
• Drugą ważną cechą, przesądzającą o wyższym poziomie abstrakcji
języków zapytań, jest to, że operatory takie mają implicite
wbudowaną iterację po kolekcjach.
• Z uwagi na to, że atrybuty i asocjacje wielowartościowe w modelu
klas deklarują unikalność swoich wartości, najczęściej będziemy
mieć do czynienia ze zbiorami (Set).
P.Habela, K.Subieta. SSR, Wykład 10, Folia 18
maj 2009
Najważniejsze operacje iteracyjne (1)
wyrażenie -> select(wyrażenieLogiczne)
• Odpowiada klauzuli where w większości języków.
• Zwraca kolekcję tego samego typu co źródłowa (po lewej
stronie strzałki)
• Jednak pozostawia w niej tylko te obiekty/wartości, dla
których wyrażenie logiczne dało wynik true.
wyrażenie1 -> collect(wyrażenie2)
• Jest to tzw. operator projekcji. Odpowiada klauzuli select
w SQL. Dla każdego elementu zwróconego przez
wyrażenie1 dokłada do finalnego rezultatu wynik
wyrażenia2.
P.Habela, K.Subieta. SSR, Wykład 10, Folia 19
maj 2009
Najważniejsze operacje iteracyjne (2)
• Moglibyśmy zatem napisać:
(pracownicy -> collect(pracujeW)) -> collect(nazwaDzialu)
• Jednak dla wyrażeń ścieżkowych mamy do dyspozycji
postać uproszczoną. Można po prostu napisać:
pracownicy.pracujeW.nazwaDzialu
– Zwróćmy uwagę, że część pracownicy.pracujeW zwraca obiekty
Dział. Nie jest to widoczne eksplicite.
• Jawnie skorzystać z operatora collect musimy natomiast,
jeśli projekcja następuje na złożone wyrażenie. Np.
zamowienie.pozycja -> collect(ileSztuk * zamawianyProdukt.cena)
P.Habela, K.Subieta. SSR, Wykład 10, Folia 20
maj 2009
Najważniejsze operacje iteracyjne (3)
• Pozostałe istotne operacje iteracyjne obejmują:
wyrażenie1 -> exists(wyrażenie2)
• Jest to kwantyfikator szczegółowy.
– wyrażenie2 powinno zwracać wartość logiczną.
wyrażenie1 -> forAll(wyrażenie2)
• Jest to kwantyfikator ogólny.
– wyrażenie2 powinno zwracać wartość logiczną.
wyrażenie1 -> sortedBy(wyrażenie2)
• Jest to zapytanie sortujące wynik zapytania wyrażenie1.
– wyrażenie2 powinno zwracać wartość typu, na którym dostępny
jest operator porównania liniowego, np. < dla liczb, porządek
leksykograficzny dla stringów, itp.
P.Habela, K.Subieta. SSR, Wykład 10, Folia 21
maj 2009
Pomocnicze zmienne, operator count
• We wszystkich operatorach iteracyjnych można powołać zmienną
pomocniczą:
pracownicy -> select(p | p.placa<2000)
• Mamy również do dyspozycji operację
->count( wyrażenie)
• która zwraca liczbę wystąpień elementów kolekcji przed ->
identycznych z podanym parametrem.
– Takie porównanie może dotyczyć wartości typów prostych, albo
identyfikatorów obiektów.
– Np. załóżmy, że nowyPrac zwraca identyfikator obiektu pracownik
– Przed dodaniem takiego obiektu do kolekcji możemy dokonać sprawdzenia,
czy nie jest on już obecny w kolekcji.
pracownicy -> count(nowyPrac)
– Jeżeli nowyPrac nie jest obecny w kolekcji, to powyższe wyrażenie zwróci 0.
• Zauważmy, że dzięki zmiennej pomocniczej, operację ->count
można zastąpić wyrażeniem z operacjami ->select i ->size , np.
(pracownicy -> select(x | x = nowyPrac)) -> size()
P.Habela, K.Subieta. SSR, Wykład 10, Folia 22
maj 2009
Inne operacje na kolekcjach
• Podobne składniowo są zwykłe operacje na kolekcjach. Poniżej ważniejsze:
wyrażenie -> asSet()
• Eliminuje duplikaty w kolekcji zwróconej przez poprzedzające wyrażenie.
wyrażenie -> isEmpty()
wyrażenie -> notEmpty()
• Bada, czy rezultat jest pusty / niepusty.
wyrażenie -> first()
wyrażenie -> last()
wyrażenie -> subSequence( dolny, górny)
wyrażenie -> subOrderedSet(dolny, górny)
• Operacje wyodrębniania części kolekcji uporządkowanej.
wyrażenie -> size()
wyrażenie -> max()
wyrażenie -> min()
wyrażenie -> sum()
• Zwracają odpowiednio liczebność rezultatu, maksymalną i minimalną wartość
oraz sumę (te 3 ostatnie wymagają odpowiedniego typu kolekcji).
– Odpowiedniki count, max, min i sum w SQL
P.Habela, K.Subieta. SSR, Wykład 10, Folia 23
maj 2009
Obsługa dat
• Wartości typu Date można porównywać ( =, >, >=, <, <=) oraz
odczytywać i zapisywać w atrybutach i zmiennych lokalnych
mających ten typ.
• Literał daty możemy zapisać w formie skróconej, np. 2008-11-25
• albo w formie pełnej, np. 2008-11-25 17:00:00.001
• Celem uzyskania bieżącej daty i godziny systemowej, należy się
posłużyć operacją now() .
– Ponieważ nie jest ona funkcją, ale operacją, wymaga jakiegoś podmiotu
wywołania.
• Możemy to zrobić pisząc: jakaśZmiennaAlboAtrybutTypuDate.now()
• Można podstawić dowolną, ręcznie wpisaną datę: 2008-11-25.now
–
–
–
–
Nie jest to zbytnio logiczne
Funkcjonalność oferowana przez typ Date jest dość skromna
Nie kwalifikuje się jeszcze do zastosowań produkcyjnych
Należałoby tu rozważyć wprowadzenie odcinków czasu, operatorów
dodawania i odejmowania (np. data za tydzień od…), a także stref czasowych.
P.Habela, K.Subieta. SSR, Wykład 10, Folia 24
maj 2009