Diagramy stanów UML

17 marca 2022
Jakub Rojek Jakub Rojek
Zdjęcie autorstwa Startup Stock Photos z Pexels (https://www.pexels.com/pl-pl/zdjecie/mezczyzna-osoba-rece-biuro-7367/)
Kategorie: Analiza IT, Programowanie, Podstawy IT

Na blogu omawialiśmy już same podstawy UML-a, ale wypada zrobić krótkie przypomnienie - jest to notacja, która ułatwia przygotowanie diagramów prezentujących model oprogramowania pod kątem różnych aspektów. Możemy w ten sposób pokazać zarówno strukturę, jak i przepływ informacji czy interakcje pomiędzy częściami systemu. W podlinkowanym artykule wspominaliśmy też o tym, że często mylnie utożsamia się diagramy UML-owe z diagramami klas. To błąd, gdyż ta notacja ma znacznie szerszy zakres i dzisiaj to sobie udowodnimy.

Przed nami opis diagramów stanów jako kolejnego przykładu modeli opisujących świat rzeczywisty na potrzeby IT. I spokojnie - będzie to znacznie krótszy tekst niż poprzedni. Zwróćcie też uwagę, że mimo iż będziemy opowiadać o diagramie, to jest to temat ogólnie związany z projektowaniem przepływu stanów danego obiektu w systemie - coś, co robi się bardzo często podczas etapu analizy oprogramowania.

Najprostsze zastosowanie

Wyobraźmy sobie księgarnię internetową, w której użytkownicy mogą kupować książki. Każda pozycja może być w sklepie dostępna, następnie dodana do koszyka i wreszcie zamawiana. Jednak na tym nie kończy się cykl życia tomu - użytkownik może jeszcze śledzić status jego wysyłki i doręczenia, czekając z niecierpliwością na swój zakup. Widzimy zatem, że książka ma swój stan (lub status), który się zmienia pod wpływem pewnych zdarzeń. Układ możliwych stanów dla obiektu wraz z przejściami pokazującymi momenty, w których status ulega modyfikacji, to maszyna stanowa. Diagram stanów to zatem nic innego, jak ilustracja maszyny stanowej i przykład diagramu UML z grupy behawioralnych.

Pokażmy sobie od razu ilustrację powyższej sytuacji:

Prosty diagram stanów dla książki w księgarni internetowej

Każdy diagram stanów ma swój punkt początkowy (czarne kółko) oraz co najmniej jeden punkt końcowy (czarne kółko, ale z obwódką). To "co najmniej jeden" wynika z faktu, że czasami ze względów estetycznych punktów końcowych na diagramie może być wiele i wszystkie mają równoważne znaczenie. Same stany oznaczone są prostokątami z zaokrąglonymi rogami, a od jednego do drugiego prowadzą tranzycje, czyli strzałki z informacją o rodzaju przejścia. W najprostszym przypadku jest to czynność, po wykonaniu której następuje tranzycja do danego stanu. Oczywiście, z jednego stanu można przejść do wielu, podobnie jak z wielu stanów można uzyskać jeden - powyższy przykład jest akurat bardzo prosty.

Stany złożone i inne możliwości

Mechanizm, który pozwala na nieco więcej w przypadku diagramu stanów, to tzw. stan złożony. Najogólniej mówiąc, jest to maszyna stanowa zawarta w innej maszynie stanowej - jak najbardziej ma swój punkt początkowy, jednak z każdego stanu możemy wyjść na zewnątrz, np. do punktu końcowego dla bazowej maszyny. Stany złożone mogą posłużyć do następujących operacji:

  • zwyczajne poprawienie estetyki diagramu, gdy tranzycji jest wiele lub pewne schematy się powtarzają,
  • podzielenie stanów na grupy, między którymi zachodzą przejścia i dopiero po ich zakończeniu następuje dalszy transfer stanu,
  • konieczne jest pamiętanie, jaki był podstan po powrocie do stanu złożonego.

Szczególnie ciekawy jest ostatni punkt - czasami zachodzi potrzeba powrotu do stanu złożonego, ale do podstanu, w którym program znajdował się w momencie jego opuszczania. Wówczas stosujemy historię (można spotkać się też z określeniem "płytka"), którą oznacza się poprzez okrąg z "H" w środku. Istnieje też możliwość zastosowania tzw. głębokiej historii - wyobraźmy sobie bowiem, że stany złożone mogą mieć podstany, które same w sobie są stanami złożonymi i zawierają swoje podstany. Wówczas, gdy chcemy przedstawić, iż przepływ powinien przejść dokładnie do odpowiedniego podstanu bez względu na poziom zagnieżdżenia, wystarczy dopisać gwiazdkę do "H" uzyskując "H*".

Zobaczmy przykład dla przepływu stanów w Feedybacky, a przynajmniej w trochę skróconej wersji. Przy okazji będziemy mogli pokazać jeszcze dwa mechanizmy pozwalający "skomplikować" nam diagram.

Uproszczony diagram stanów dla statusów zgłoszenia w Feedybacky

Na powyższym diagramie widzimy, że po wystartowaniu zgłoszenia, w momencie przyjęcia go przez zespół IT, może powstać seria pytań i odpowiedzi. Mamy zatem stan złożony dla grupy statusów "w toku", który składa się z trzech pozycji - "pytanie do klienta", "pytanie do kierownika" i faktyczne "w toku". Kierownik po przyjęciu zgłoszenia może mieć do niego pytania i wówczas zmieni status na ten pierwszy. Ale żeby pokazać, w jakiej sytuacji się to dzieje, wykorzystujemy instrukcję warunkową sygnalizowaną przez kwadrat obrócony o 45 stopni. Służy on do rozgałęzienia przepływu i może być znajomy dla osób ucząc się kiedyś schematów blokowych. Warto zauważyć, że warunek zapisany jest w nawiasach kwadratowych, aby odróżnić tę notę od zwykłej nazwy przejścia. Po przebyciu pętli pytań do klienta i później do kierownika, zgłoszenie może być uznane za wstrzymane ("Paused"). Widzimy tutaj, że po decyzji o wznowieniu przepływ jest kierowany do ostatniego znanego podstanu w stanie złożonym "w toku" lub zgłoszenie po prostu kasowane. Natomiast, jeśli zgłoszenie rzeczywiście jest realizowane i ostatecznie się kończy, to przechodzimy do stanu "wdrożone". I tutaj mamy kolejną "nowinkę" - rozszerzony opis tranzycji. Ma on następującą postać:

wyzwalacz [dozór] / akcja ^ wywołanie zdarzenia

Może to być zamiennik instrukcji warunkowej (i w takiej roli znajduje się na powyższym diagramie), ale także wskazywać kolejne akcje i zdarzenia, jakie występują podczas przejścia. W przykładzie na diagramie mamy decyzję klienta w sprawie poprawki - jeśli ma uwagi, to wracamy do podstanu, ale konkretnie do statusu "pytanie do kierownika". Jeśli jednak wszystko jest w porządku - przechodzimy "lewą stroną" i przy okazji oznaczamy zgłoszenie jako ukończone, a jego historię uznajemy za zamkniętą.

Bywają też sytuacje, w których obiekt może być jednocześnie w dwóch lub więcej stanach, przy czym należą one do odrębnych grup. Przykładowo, książka w sklepie może być w różnym stopniu zachowania (bez szkód, uszkodzony itp.), ale jednocześnie może być też niezapłacona lub zapłacona (czyli posiadać stan związany z opłaceniem produktu). Takie sytuacje nazywamy stanami równoległymi i oznaczamy je za pomocą prostokąta podzielonego na dwa obszary przedzielone linią przerywaną. Przypominają one trochę podstany, ale jest to jednak coś innego.

Ilustracja stanów równoległych w diagramie stanów UML

Wreszcie, opis samych stanów może zostać rozszerzony o kolejne dyrektywy, które konkretyzują zachowanie się obiektu w różnych sytuacjach. Po każdej dyrektywie umieszczany jest slash, a podawana uruchamiana akcja lub zdarzenie (to drugie poprzedza się daszkiem (^)). Dostępne możliwości to:

  • entry - akcja wywoływana jest po wejściu w dany stan (odpowiednik konstruktora).
  • do - akcja wywoływana jest w trakcie przebywania obiektu w danym stanie (w wiecznej pętli).
  • exit - akcja wywoływana jest przy wyjściu z danego stanu (odpowiednik destruktora).
  • event nazwa_zdarzenia - akcja wywoływana jest w momencie wystąpienia danego zdarzenia.

Poniżej przykład takiego opisu. Zwróćcie uwagę na użycie daszka (^) przy "state review" - jest to wywołanie zdarzenia i także tutaj notacja jest spójna z wcześniej przywołaną.

Dyrektywy stanu w UML

Podsumowanie

Przedstawiliśmy drugi z najczęściej wykorzystywanych diagramów UML - diagram stanów, pozwalający zamodelować przepływ statusu obiektu w systemie IT. A nie ukrywajmy, że w większości dużych aplikacji znajdą się obiekty, które są opisywane przez tę własność. I podobnie, jak w przypadku diagramu klas, nie zawsze w dokumentacji do opisu stanów w pełni korzysta się z notacji UML, ale może być ona przydatna, aby zamodelować coś, czego opis zająłby wiele stron.

Pozdrawiam i dziękuję - Jakub Rojek.

Piszemy nie tylko artykuły na blogu, ale też aplikacje i dokumentację dla naszych klientów. Zobacz, z kim do tej pory współpracowaliśmy.

O autorze

Jakub Rojek

Główny programista i współwłaściciel Wilda Software, z wieloletnim doświadczeniem w tworzeniu i rozwoju oprogramowania, ale także w pisaniu tekstów na różnorakich blogach. Zaprawiony w boju analityk i architekt systemów IT. Jednocześnie absolwent Politechniki Poznańskiej i okazjonalny prowadzący zajęcia na tej uczelni. W wolnych chwilach oddaje się graniu w gry wideo (głównie w karcianki), czytaniu książek, oglądaniu futbolu amerykańskiego i e-sportu, odkrywaniu cięższej muzyki oraz wytykaniu innym błędów językowych.

Jakub Rojek