Obiekty na diagramie (UML)

Na przykładowym diagramie klas znajdowala się klasa „Klient”. Na podstawie tej klasy mamy obiekt „DaneKlienta”, który został zamieszczony na poniższym diagramie – diagramie czynności (ang. activity diagram).

Natomiast na drugim przykładowym diagramie jest obiekt „DaneZamowienia”:

Diagram klas (UML) po raz 2

W związku z tym, że poprzedni diagram klas wywołał dyskusję na pewnym forum nt. jego zawartości, konstrukcji, zastosowanych elementów, atrybutów oraz realizacji między nimi, przygotowany został nowy diagram (poniżej). Dyskusja jak najbardziej konstruktywna (miejmy nadzieję, że dla wszystkich czytelników), została zapoczątkowana przez Analityka Biznesowgo, którego wizyta na moim blogu mnie bardzo zaskoczyła. Zamieszczam poniższy diagram z komentarzem i czekam na dalszy ciąg. Na tym blogu zawarte są treści, które są analizowane i później proponowane są nowe rozwiązania – taka jest idea tego bloga („modelowanie trochę od innej strony…”). Tym razem zostałem w swoich planach wyprzedzony z analizą przykładu.

Kilka komentarzy:

  • nie zastosowano w nim kompozycji, ponieważ jest za mocną relacją, nie oddaje rzeczywistości. W szczególności w tego typu dziedzinie Klient może istnieć samodzielnie, nie musi złożyć zamówienia, może tylko przeglądać. Podobnie produkt może istnieć samodzielnie. Diagram dotyczy dziedziny zamówień przez internet i wtedy produkt w dziedzinie istnieje przed klientem i przed zamówieniem;
  • atrybut „płeć” jest jak najbardziej przydatnym elementem w prezentowaniu Klienta, w szczególności w diagramach, które odpowiadają systemom zakładającym takie coś jak personalizacja, czy badanie zachowań klientów. Nie ma tego na diagramie, bo to nie było celem samym w sobie;
  • niektóre atrybuty rzeczywiście były zbędne lub umieszczone w nieprawidłowej klasie. W związku z tym, niektóre usunąłem, a niektóre przeniosłem;
  • zmienione zostały klasy uczestniczące w relacji dziedziczenia;
  • na diagramie warto byłoby dodać, dla jasności sytuacji, klasy użytkownika oraz interfejs koszyk, żeby lepiej zilustrować charakter;
  • można dodać klasę faktura;

Diagram klas (UML)

Na diagramach prezentujących warstwę reiżnynierii zostały zamieszczone informacje o następujących obiektach:

  • DaneKlienta – zaprezentowane jako klasa „Klient” na poniższym diagramie;
  • DaneZamowienia – zaprezentowane jako klasa „Zamowienie” na poniższym diagramie;
  • DaneDostawy – zaprezentowane jako klasa „Dostawa” na poniższym diagramie;

Były one używane do realizacji określonych czynności. Na poniższym diagramie – tzw. diagramie klas (ang. class diagram)zostały one zaprazentowane jako klasy wraz z przykładowymi atrybutami (ang. attribute) oraz operacjami (ang. operation). Dodatkowo zostały umieszczone dodatkowe klasy – np. Płatność – silnie powiązane z tą dziedziną. Między poszczególnymi klasami zostały określone związki. Przy niektórych podana liczebność. Inne natomiast są w relacji uogólnienia.

Powyższy diagram ma charakter przykładowy. Kolejna wersja diagramu z komentarzami znajduje się tutaj.

Diagram przypadków użycia (UML)

W czasie tworzenia systemu informacyjnego – w tym przypadku systemu wspierającego złożenie zamówienia przez Klienta i jego realizację przez dostawcę – istotne jest to, aby spojrzeć na:

  1. czynności wykonywane przez klientów – np. „Określenie zamówienia” czy wybranie produktu, określenie jego ilości, „Realizacja zamówienia”, czyli określenie w jaki sposób ma do nas trafić i kiedy/jak za produkt chemy zapłacić
  2. czynności wykonywane przez pracowników dostawcy – np. „Analiza zamówienia” czy „Realizacja wysyłki”.

Każdą z tych przykładowych czynności można dalej rozbijać, identyfikując jej elementy składowe. Przykładowe powiązanie aktorów (klient, pracownik) oraz wykonywanych czynności jest przedstawione na poniższym diagramie – tzw. diagram przypadków użycia (ang. use case diagram).

Współdzielenie listy zakupów – „pull” czy „push”?

W ramach poprzedniego wpisu dotyczącego elektronicznej listy zakupów wskazałem, że jedną z funkcjonalności dostępnych w aplikacjach wspierających zakupy, jest możliwość współdzielenia listy zakupów. W aplikacji, z której korzystałem, to współdzielenie oparte było o podawanie adresu e-mail, przy założeniu, że zarówno tworzący listę, jak i ją otrzymujący jest zarejestrowanym użytkownikiem aplikacji i ma założone konto na serwerze aplikacji.

Użytkownicy aplikacji w ramach udostępnionej listy mogą:

  • dodawać/edytować produkty na liście,
  • śledzić postęp zakupów realizowanych przez drugą osobę,
  • kopiować listę na przyszłość.

Założeniem dla funkcjonowania współdzielenia listy zakupów, był dostęp do sieci Internet z poziomu urządzenia, na którym jest zainstalowana aplikacja. W sytuacji utraty dostępu do sieci Internet, zmiany na liście oczekiwały na podłączenie się drugiego użytkownika listy. W przypadku dostępu do sieci Internet przez obydwa urządzenia, zmiany były przekazywane w miarę na bieżąco.

Wspólny proces dla obydwu użytkowników jest przedstawiony na poniższym diagramie.

wspoldzielenie450px_1

Dla współdzielenia listy można byłoby zastosować dwa modele „współpracy”:

  • model „push” (aplikacja inicjująca zmianę przy pomocy pośrednika przekazuje zmiany do drugiej aplikacji)
  • model „pull” (jedna aplikacja przekazuje zmiany do wspólnego miejsca, a druga odpytuje o ewentualne zmiany w liście)

Wariant: model „push
Użytkownik – właściciel listy – rejestruje listę, udostępnia ją, a następnie wprowadza na niej zmiany. Wszystkie te elementy są przesyłane na serwer obsługujący aplikację. Każda zmiana jest rejestrowana i przekazywana do aplikacji odbiorcy listy (wskazanego poprzez e-mail). Serwer wysyła informację o zmianach do aplikacji użytkownika, rejestrując wysłane zmiany. Ewentualna zmiana przez drugą aplikację jest wysyłana analogiczną ścieżką. Na diagramie przedstawiona została ta komunikacja, gdzie każda zmiana jest „pchana” (ang. push) na serwer a następnie do drugiej aplikacji.

wspoldzielenie450px_2

W tym celu został wykorzystany wykres wyglądający jak diagram sekwencji (znany z UML) z pewnymi dodatkami. W projektowaniu rozwiązań można znaleźć wzorzec fire-and-forget, w którym informacje są przesyłane bez oczekiwania na reakcję odbiorcy. Trochę jak tutaj, bo w tym modelu aplikacja nie czeka na informację zwrotną.

Wariant: model „pull
Podobnie jak w poprzednim wariancie, użytkownik – właściciel listy – rejestruje listę, udostępnia ją, a następnie wprowadza na niej zmiany. Wszystkie te elementy są przesyłane na serwer obsługujący aplikację. Trafiają na listę zmian/działań (ang. queue) wykonanych na liście zakupów w postaci zdarzeń typu: udostępnienie listy, zmiana listy, zakup produktu (a dokładnie jego odklikanie) itd. W momencie ustawienia udostępnienia listy, zmiany na utworzonej liście są dostępne do pobrania/odczytania (ang. pull) przez drugą aplikację. W sytuacji, gdy takie udostępnienie nie nastąpi, zmiany są odnotowywane na serwerze i są dostępne tylko dla właściciela listy.

wspoldzielenie450px_3

Na diagramie jest przedstawiony ten model. W projektowaniu rozwiązań można znaleźć wzorzec publish-subscribe, który zakłada, że odbiorcy pobierają interesujące ich informacje z miejsca, gdzie zostały wystawione.

Pewnie można byłoby jeszcze zidentyfikować kilka innych wariantów, gdzie ta komunikacja jest bardziej dwustronna. Jednakże biorąc pod fakt, że aplikacja jest zainstalowana na urządzeniach mobilnych i nie w każdej sytuacji aplikacje będą miały dostęp do internetu, jakiś sposób przechowywania i kolejkowania zmian jest konieczny.  Dodatkowo użytkownicy wykonują często operacje zmiany i przeglądania zmian w różnym czasie lub tworzenie listy jest rozłożone w czasie. Patrząc od strony „odbiorcy” listy, także może zmodyfikować listę w czasie jej dostępności. Takie zmiany są „przesyłane zwrotnie” do właściciela listy.

Proces oparty o stany

Wyobraźmy sobie, że parking opisany ostatnio we wpisie, wykonuje oprócz wskazanych 3 podstawowych działań (przyjęcie samochodu, przyjęcie opłaty i wypuszczenie samochodu), jeszcze jedną bardzo ważną rzecz. Mianowicie, wskazuje w momencie wjazdu na parking, gdzie jest najbliższe wolne miejsce. Niby rzecz trywialna, ale wymaga odpowiedniego przygotowania – zarówno od strony infrastruktury (kontrola zajętych miejsc, identyfikacja gdzie są samochody wcześniej wpuszczone, które jeszcze nie zaparkowały), jak i informatycznej (odpowiedni system zarządzania miejscami parkingowymi, kontrolą stanów miejsc i ich zmianą, a także prezentacją graficzną parkingu).

stany450px

Z jednej strony mamy zmianę stanu miejsca parkingowego, a z drugiej weryfikację położenia, gdzie jest wolne miejsce. Takie procesy zostały zaprezentowane na powyższym diagramie. W momencie realizacji procesu (na diagramie Określ mapę dojazdu) z prezentacją najbliższego wolnego miejsca (na diagramie Wyświetl mapę dojazdu) system musi wziąć pod uwagę stany miejsc parkingowych wynikajacych z:

  • zajęcia miejsca parkingowego (zmiana stanu na zajęty),
  • opuszczenia miejsca parkingowego (zmiana statusu na wolny),
  • przemieszczania się samochodów wpuszczonych na parking (w dowolnym momencie dane miejsce może zmienić stan),
  • lokalizacji samochodów wpuszczonych na parking (np. są na 2 poziomie, a miejsce akurat jest na 1 poziomie).

Chcąc uniknąć ryzyka, że wskazane najbliższem miejsce okaże się zajęte, można także wskazywać miejsce alternatywne. Dodatkowo już po wjeździe na parking w widocznych miejscach mogłyby być umieszczone ekrany wskazujące na najbliższe wolne miejsce parkingowe (cykliczne wykonywanie pierwszego procesu). Ułatwieniem w zarządzaniu takim parkingiem mogłoby być to, że nie da się cofnąć do innego miejsca parkingowego, nie przejeżdżając przez punkt startowy, który dodatkowo jest kontrolowany przez system.

W podanym przykładzie podane procesy opierają się na świadomym wykorzystaniu stanów poszczególnych miejsc parkingowych (oznaczone za pomocą funkcji w klasach UML na diagramie). Stany miejsc występują naprzemiennie i trudno jest określić jakie są ramy czasowe dla rozpoczęcia procesu i jego zakończenia. Dla danego miejsca stan może się nie zmienić godzinami lub zmienić się raz i pozostać taki przez wiele godzin. Takie cechy są charakterystyczne dla procesów sterowanych stanami – ang. state-driven process. Skierowanie samochodu na dane miejsce jest możliwe tylko, gdy jest ono wolne. Zmiana stanu odbywa się poprzez odnotowanie wjazdu samochodu na dane miejsce.

Co dostarczają kroki procesu?

Ostatnio spotkałem się z pojęciem diagramu procesu, który na bazie kroków procesu równocześnie wskazuje, co one dostarczają. Jest to tzw. Process-Deliverable Diagram (w skrócie PDD). Jest to diagram, który łączy w sobie elementy z dwóch obszarów:

  • kroków/czynności składających się na proces, zaprezentowanych za pomocą diagramu czynności (ang. activity diagram) z notacji UML,
  • obiektów dostarczanych przez proces, zaprezentowanych za pomocą diagramu klas (ang. class diagram) z notacji UML;

pdd2

Pierwszym procesem, który przyszedł mi na myśl, gdy przeczytałem o tym diagramie, był proces realizacji projektu, zaprezentowany na powyższym przykładowym diagramie. Począwszy od inicjalizacji projektu, przez realizację jego kolejnych działań po wdrożenie, na każdym etapie dostarczany (ang. deliver) jest określony “produkt”. Produkty w całości lub częściowo są wykorzystywane na kolejnych etapach procesu. Te produkty można potraktować jako produkty końcowe procesu. Mogą być np. podstawą kolejnych zmian, przeprowadzania szkoleń, wyjaśniania reklamacji, analizy luk procesu.

Podobne rozwiązanie zostało zastosowane przy prezentacji wejść i wyjść w ramach realizacji procesu rekrutacji.

Proces w czarnej skrzynce

Dotychczas system karty aglomeracyjnej traktowałem jak tzw. czarną skrzynkę (ang. black box). Interesowały mnie tylko te działania, które mają skutek na wyświetlane komunikaty i reakcje na działania użytkownika. Wskazywałem początek trasy, koniec trasy i otrzymywałem zwrotnie odpowiedni komunikat.

Zastanówmy się jednak jak proces przedstawiony w jednym z poprzednich wpisów, a dokładnie o komunikatach dla odbiorcy wygląda w systemie karty. Można powiedzieć, że ma na pewno operacje: przyjmij punkt trasy, wyświetl komunikat, nalicz opłatę. Można sobie wyobrazić, że ma również sprawdź czy nastąpiła przesiadka (momencie odczytu punktu trasy), sprawdź czas między punktami trasy (aby określić, czy to jest kontynuacja trasy czy niezależna podróż).

Karta450

Na powyższym diagramie górna jego część prezentuje wybrane działania i ich skutek. Dodatkowo zostały wpisane obiekty, które są tworzone. Obiekty te są odpowiednikiem klas zaprezentowanych poniżej, za pomocą UML. Przedstawiony diagram obrazuje co potencjalnie może się dziać w ramach systemu i jakie struktury są w tym celu potrzebne. Na przykład, po „przyjmij punkt trasy” następuje sprawdzenie, czy to początek, czy koniec, sprawdzenie czy nastąpiła przesiadka, następnie zapis informacji z jego parametrami, następnie naliczenie opłaty i wyświetlenie komunikatu.

Krok „Określ rodzaj punktu”, po określeniu numeru linii i numeru przystanku, sprawdza, dla obecnie zapisanej trasy, (jeżeli istnieje), jaki rodzaj może to być punktu – początek, koniec czy przesiadka – są to rodzaje Punktu trasy, stąd zastosowane dziedziczenie. Określa te parametry, które zostają zapisane jako element trasy. Następnie następuje w kroku „Nalicz opłatę” żądanie Określ opłatę(), które w zależności od parametrów trasy, nalicza opłatę do końca linii, aktualizuje opłatę dla przesiadki lub określa końcową opłatę za przejechany odcinek. Naliczona opłata lub różnica do zwrotu zostaje odnotowana w saldzie karty poprzez operację ZmienSaldo().

Atrybut Linia określa na jakiej linii a atrybut przystanek określa kiedy i na jakim przystanku nastąpiło wskazanie punktu trasy. Atrybut Linia jest potrzebny do określenia czy nastąpiła przesiadka, a Przystanek (numer, czas) wskazuje, czy może traktować kolejny punkt jako przesiadkę czy niezależne rozpoczęcie trasy. Trasa określana jest przez poszczególne Punkty trasy. „Aktualna trasa” jest zapisywana dla danej Karty raz w systemie.

 

Wzorcowa pizza

Wyobraźmy sobie, że jesteśmy w pizzerii i mamy ochotę zjeść pizzę. Nie wiemy jeszcze jaką, ale będzie to pizza. Można powiedzieć, że danie to jest pewien sposób abstrakcyjne, bliżej nieokreślone, do momentu, gdy zapoznamy się z menu i dowiemy się jakie są możliwości. Spośród rodzajów pizzy, np. Amerykańska, Wegetarianska, Hawajska, Rzeznicka itp., będziemy wybierać konkretną na podstawie jej składników, które w niej będą. Przeglądamy menu i decydujemy się na konkretne składniki – na przykład Ser, Szynka, Salami lub Ananans, Ser itd.

Wymienione składniki też są w pewien sposób abstrakcyjne, nie wiemy jak będą ułożone, ile ich będzie, jak będą duże, ale wiemy, że będzie to określony składnik.
Mamy więc od strony Klienta: wybraną pizzę składająca się z wymienionych składników.
Restauracja wie jaka pizza z jakich się składa elementów, ale dopiero w momencie przygotowania nastąpi ich wybór.

Powyższy przykład, opierając się na wyborze abstrakcyjnych składników z jednej strony, a dostarczenia konkretnych produktów z drugiej, można przedstawić za pomocą wzorca projektowego fabryka abstrakcyjna (ang. abstract factory).
Wzorzec ten składa się z elementów, zaprezentowanych na powyższym przykładowym diagramie UML:

  • Fabryka abstrakcyjna (ang. abstract factory) – interfejs dla klienta do wyboru konkretnych rodzajów pizzy. Na diagramie oznaczony jako „AF”.
  • Fabryka konkretna (ang. concrect factory) – określa elementy konieczne do stworzenia danej pizzy z elementów składowych. Na diagramie oznaczony jako „CF”.
  • Produkt abstrakcyjny (ang. abstract product) – interfejst dla klienta do wyboru rodzaju składników pizzy. Na diagramie oznaczony jako „AP”.
  • Produkt (ang. product) – konkretna realizacja oczekiwana przez klienta – dany składnik pizzy. Na diagramie oznaczony jako „P”.
  • Klient (ang. client) – używa powyższych elementów (wybiera pizzę, składniki). Na diagramie oznaczony jako „C”.

Klient inicjuje wybór pizzy, wybierając jej składniki, fabryka tworzy, w zależności od wybranej pizzy, odpowiednie zestawienie składników. Te zależności obrazują strzałki użytkowania (ang. uses) elementów na diagramie.

Proces akceptacji – sposób sterowania

Zastanawiałem się po przeczytaniu komentarzy do poprzedniego wpisu nad rozwiązaniem dla procesu akceptacji dokumentu. Wydaje się, że są dwa możliwe rozwiązania:

  1. System wspiera proces akceptacji i decyduje o tym do kogo trafia dokument po wykonaniu danej akcji. Określa również kiedy proces jest zakończony. Na poniższym diagramie górna część (Diagram A) prezentuje takie podejście. System po akcji użytkownika „Zatwierdź” lub „Odrzuć” wykonuje „Określenie osoby” i ewentualnie „Przesyła dokument”. Operacja „Prześlij dokument” może wystąpić bądź nie. Podobnie w wyniku „Określ osobę” może zostać zakończony proces.
  2. Użytkownik decyduje o osobach uczestniczących w procesie. Po wykonaniu „Akceptuj” lub „Odrzuć” użytkownik decyduje, czy przesłać dokument dalej, skonsultować go, czy całkowicie odrzucić. Rolą systemu w takiej sytuacji jest śledzenie i odnotowywanie zmian. Taka sytuacja jest przedstawiona na dolnej części poniższego diagramu (diagram B).

Na powyższym diagramie (diagram przypadków użycia w UML) za pomocą powiązań <<include>> oraz <<extend>> przedstawiono specyfikę następujących przypadków użycia:

  • „Określ osobę” – użyto powiązania<<include>>, które oznacza, że zawsze podczas „Zatwierdź dokument” oraz „Odrzuć dokument” występuje „Określ osobę”.
  • „Prześlij dokument” – użyto powiązania <<extend>>, które oznacza, że jedynie w określonych sytuacjach (przy spełnieniu ustalony warunków), zostanie wywołane „Prześlij dokument”.