Diagramy stanów UML

17 march 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/)
Categories: IT analysis, Programming, IT fundamentals

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.

We write not only blog articles, but also applications and documentation for our clients. See who we have worked with so far.

About author

Jakub Rojek

Lead programmer and co-owner of Wilda Software, with many years of experience in software creation and development, but also in writing texts for various blogs. A trained analyst and IT systems architect. At the same time he is a graduate of Poznan University of Technology and occasionally teaches at this university. In his free time, he enjoys playing video games (mainly card games), reading books, watching american football and e-sport, discovering heavier music, and pointing out other people's language mistakes.

Jakub Rojek