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