Podręcznik
W procesie wytwarzania oprogramowania jednym z kluczowych zagadnień jest określenie zakresu budowanego systemu. Wymagania użytkownika powinny opisać potrzeby klienta w sposób wystarczający do określenia rozmiaru systemu i nakładu pracy potrzebnej do jego zbudowania.
2. Specyfikowanie wymagań funkcjonalnych
2.1. Identyfikacja jednostek funkcjonalnych systemu
W typowym, interaktywnym systemie oprogramowania, cel biznesowy osiągamy w rezultacie przejścia przez ciąg interakcji użytkownika z systemem. Przykładowo, na rysunku 1 widzimy użytkownika, który chce uzyskać określony rezultat (np. zarejestrować nowego kontrahenta albo wydrukować listę kontrahentów). Aby osiągnąć ten rezultat, użytkownik musi najpierw wybrać określoną opcję w menu (pkt. 1 na rysunku). Efektem jest np. wyświetlenie formularza lub okienka na co użytkownik reaguje wprowadzeniem danych oraz wybraniem jednej z dostępnych w nowym oknie opcji (pkt. 2). Taki dialog między użytkownikiem a systemem może doprowadzić do osiągnięcia zamierzonego celu (pkt. 3 i 4). Może też zakończyć się niepowodzeniem (pkt. 3 i 5). Niepowodzenie może być spowodowane np. błędem walidacji danych lub wybraniem przez użytkownika opcji anulowania transakcji.
Rysunek 2.1: Współpraca systemu z użytkownikiem
Najczęściej stosowanymi jednostkami funkcjonalnymi prowadzącymi do uzyskania określonego rezultatu są wprowadzone już w poprzednich rozdziałach historie użytkownika oraz przypadki użycia. Punktem wyjścia do identyfikacji przypadków użycia lub historii użytkownika powinna być analiza cech systemu sformułowanych w wizji. Istotnym źródłem pomysłów na przypadki użycia może być również analiza procesów biznesowych. Podczas poszukiwania należy przede wszystkim pamiętać o podstawowych cechach charakteryzujących dobre jednostki funkcjonalne.
- Niezależność – możliwość realizacji danej funkcjonalności w jak największym stopniu niezależnie od innych;
- Negocjowalność – poziom ogólności dający możliwość ustalania szczegółów między klientem a wykonawcą;
- Cenność dla klienta – odpowiedni poziom korzyści, jakie klient (zamawiający, użytkownik) odniesie z implementacji tego wymagania;
- Szacowalność – możliwość dokonania racjonalnego szacowania rozmiarów oraz kosztu wykonania;
- Mały rozmiar – możliwość realizacji danego wymagania w stosunkowo krótkim czasie (kilka dni do kilku, np. 2-3 tygodni);
- Testowalność – możliwość udowodnienia spełnienia wymagania poprzez wykonanie testów.
Identyfikację przypadków użycia lub historii użytkownika może nam ułatwić wstępne zidentyfikowanie grup użytkowników systemu. W metodykach, które wykorzystują język UML, do tego celu wykorzystujemy aktorów. Aktor definiuje klasę obiektów spoza modelowanego systemu. Obiekty te mogą być osobami, urządzeniami lub zewnętrznym systemami informatycznymi. Możemy również powiedzieć, że aktor definiuje rolę graną przez obiekty w stosunku do danego systemu. Ważne jest to, że aktor nie może określać jakiejkolwiek części modelowanego systemu oprogramowania. W metodykach wykorzystujących historie użytkownika równoważnikiem aktorów są role użytkowników.
2.2. Historie użytkownika
Koncepcja historii użytkownika (ang. User Story) została zaproponowana po raz pierwszy przez Kenta Becka w ramach jego metodyki eXtreme Programming (XP) pod koniec lat 90. XX wieku. Od tamtego czasu, notacja i techniki związane z historiami użytkownika zyskały sporą popularność. Są one wykorzystywane w różnych projektach prowadzonych w sposób zwinny (ang. Agile), np. przy pomocy popularnej metodyki Scrum.
Treść historii użytkownika zawiera jedynie najważniejszą informację o zachowaniu się systemu z punktu widzenia przyszłego użytkownika. Historia użytkownika jedynie sygnalizuje potencjalną funkcjonalność systemu i jest punktem wyjścia do dyskusji o sposobach jej realizacji oraz walidacji. Ważne jest to, że historie użytkownika zachęcają w ten sposób do współpracy przedstawicieli klienta i wykonawcy. Oto kilka wzorców, które można zastosować pisząc historie użytkownika:
- Jako <rodzaj użytkownika> chcę <jakiś cel>, aby <jakiś powód> (np. Jako klient chcę złożyć zamówienie na samochód, aby rozpocząć proces jego zakupu.)
- <rodzaj użytkownika> chce <jakiś cel>, aby <jakiś powód> (np. Klient chce złożyć zamówienie na samochód – tu pomijamy opcjonalną część „dlaczego”.)
Na rysunku 2 przedstawiamy przykłady historii użytkownika o typowych rozmiarach. Typowo, takie historie można zrealizować w kilka dni pracy zespołu deweloperskiego.
Rysunek 2.2: Przykłady historii użytkownika
2.3. Przypadki użycia i aktorzy
Model przypadków użycia został po raz pierwszy wprowadzona przez Ivara Jacobsona w połowie lat 90. XX wieku, podobnie jak w przypadku historii użytkownika. Ivar Jacobson jest również jednym z twórców języka UML (obok Grady’ego Boocha i Jamesa Rumbaugh). Dlatego też notacja przypadków użycia jest jednym z podstawowych modeli języka UML. Podstawowymi elementami modelu przypadków użycia są aktorzy i przypadki użycia. Model zawiera również relacje między nimi.
Podstawową notacją aktora w języku UML jest ikona człowieka narysowana prostymi kreskami, co ilustruje rysunek 2.3 (ikona po lewej stronie). Tak jak w przypadku innych elementów modeli, również aktorom można nadawać stereotypy. Częstą praktyką jest nadawanie stereotypu «system» aktorom definiującym zewnętrzne systemy informatyczne (ikona w środku rysunku). Jeśli stosujemy odpowiednie narzędzie do modelowania w języku UML, nadanie stereotypu może być również związane ze zmianą kształtu ikony (ikona po prawej stronie).
Rysunek 2.3: Warianty notacji aktora
Możliwe jest stosowanie relacji generalizacji między aktorami. Przykład zastosowania tych relacji widzimy na rysunku 2.4. W przykładzie tym, najbardziej ogólnym aktorem jest „Pracownik firmy”. Pozostali aktorzy specjalizują (dziedziczą) od niego zgodnie z odpowiednio skierowanymi relacjami. Aktorzy szczegółowi dziedziczą zakres odpowiedzialności, ale mogą uczestniczyć z kolejnych przypadkach użycia, które poszerzają ich zakres odpowiedzialności.
Rysunek 2.4: Relacje generalizacji dla aktorów
Przypadek użycia definiujemy jako klasę zachowań modelowanego systemu (tzw. podmiotu), prowadzących do osiągnięcia obserwowalnego, istotnego rezultatu dla jakiegoś aktora (lub aktorów). Podstawową notacją dla przypadków użycia w języku UML jest ikona elipsy, co ilustruje rysunek 2.5. Nazwa przypadku użycia może być umieszczona pod lub wewnątrz elipsy. Jako ciekawostkę można przytoczyć wariant notacji, który stosuje ikonę klasy (po prawej stronie rysunku). Jest to rzadko spotykana notacja, która nawiązuje do faktu, że przypadki użycia są rodzajem klas.
Rysunek 2.5: Warianty notacji przypadku użycia
Każdy przypadek użycia powinien mieć jasno określony cel, który powinien wynikać z jego nazwy. Warto tutaj przestrzegać określonych konwencji, które ułatwiają określenie celu przypadku użycia. Ważne jest, aby nazwy były pisane w jednolitej formie. Można na przykład przyjąć formę, w której stosujemy rzeczowniki odczasownikowe (np. „Zarejestrowanie pojazdu”, „Dodanie użytkownika”, „Pokazanie …”, „Obsłużenie …”). Inną formą jest forma polecenia (np. „Zarejestruj pojazd”, „Dodaj użytkownika”, „Pokaż …”, „Obsłuż …”).
Na diagramach przypadków użycia umieszczamy aktorów i przypadki użycia wraz z łączącymi je relacjami. Na rysunku 2.6 widzimy diagram zawierający przykłady takich relacji. Najczęściej spotykaną jest relacja asocjacji między aktorem i przypadkiem użycia. Relacja ta oznacza możliwość uczestnictwa danego aktora w powiązanym z nim przypadku użycia. W ramach takiego uczestnictwa, aktor może być tzw. aktorem głównym. Oznacza to, że aktor uruchamia dany przypadek użycia i jest on realizowany na jego rzecz.
Rysunek 2.6: Przykładowy diagram przypadków użycia
Aktor może również pełnić rolę poboczną (pomocniczą) w relacji z przypadkiem użycia. Oznacza to, że aktor zaczyna brać udział dopiero w dalszych krokach scenariuszy danego przypadku użycia. Przykładowo, może to oznaczać, że system w pewnym momencie wyświetla na ekranie aktora pobocznego monit (np. komunikat o konieczności potwierdzenia operacji). Aktor musi wtedy zareagować na taki monit, aby wykonanie przypadku użycia mogło być kontynuowane. Przykładem na rysunku 8 są relacje przypadku użycia „Obsłużenie płatności zaliczki” z aktorami „Klient” i „System bankowy
2.4. Relacje między przypadkami użycia
Oprócz relacji między aktorami i przypadkami użycia często stosowane są relacje między przypadkami użycia. Język UML standardowo definiuje dwie relacje zależności – relację «include» i relację «extend». Dzięki temu możliwe jest bardziej precyzyjne wyróżnienie poszczególnych przypadków użycia. Możliwe jest również zastosowanie przypadków użycia w różnych kontekstach. Relacje «include» i «extend» ilustruje rysunek 2.7, który jest rozwinięciem rysunku 2.6.
Rysunek 2.7: Model przypadków użycia z relacjami «include» i «extend»
Relacja «include» („wstawienie”) oznacza bezwarunkowe włączenie w treść scenariuszy jednego przypadku użycia, treści scenariuszy innego. Na rysunku 2.7 widzimy jedną taką relację, oznaczoną przerywaną strzałką z odpowiednim stereotypem.
Rysunek 2.8: Włączenie treści przypadku użycia dla relacji «include»
Zasadę działania relacji «include» ilustruje rysunek 2.8. Na rysunku, interakcje (kroki scenariuszy) symbolizują kropki. W naszym przykładzie, cała treść przypadku użycia po prawej stronie jest wstawiona w odpowiednim punkcie treści drugiego przypadku użycia (po lewej stronie). Zwróćmy uwagę na to, że wstawienie treści nie podlega żadnym warunkom. Jednocześnie, należy podkreślić, że wykonanie przypadku użycia włączającego (tu: „Wyszukanie zamówienia”) nie musi zawsze zawierać wykonania przypadku użycia włączanego (tu: „Pokazanie historii …”). Możliwe jest wykonanie zgodne z jednym ze scenariuszy alternatywnych, który nie zawiera włączenia
Druga z relacji to relacja «extend» („rozszerzenie”). Oznacza ona możliwość wplecenia treści scenariuszy przypadku użycia rozszerzającego w treść scenariuszy przypadku użycia rozszerzanego. Możliwość wplecenia może podlegać określonym warunkom, które powinny być dołączone do specyfikacji danej relacji «extend».
Przypadek użycia rozszerzający może mieć kilka części wplatanych w przypadki rozszerzane. Przykład takiej sytuacji ilustruje rysunek 2.9. Widzimy tutaj, że przypadek użycia „Obsłużenie stałego klienta” rozszerza przypadek użycia „Zarejestrowanie wydania samochodu”. Rozszerzenie polega na warunkowym wpleceniu dwóch fragmentów funkcjonalności do treści przypadku rozszerzanego. Warunkiem wplecenia jest to, że klient jest zarejestrowany, czyli jest stałym klientem. Pierwszy fragment dotyczy przypomnienia na początku rejestrowania wydania samochodu, że klient jest stałym klientem. Drugi fragment dotyczy możliwości wprowadzanie danych karty rabatowej w celu otrzymania punktów rabatowych.
Rysunek 2.9: Wplecenie treści przypadku użycia dla relacji «extend»
Semantyka relacji «include» i «extend» oparta jest na bezwarunkowym lub warunkowym wstawianiu treści jednych przypadków użycia w treść innych. Zrozumienie tej semantyki często sprawia kłopoty. W szczególności, często popełnianym błędem jest niewłaściwe skierowanie relacji. Z tego powodu powstała propozycja alternatywy - relacji «invoke». Relacja ta ma semantykę podobną do semantyki wywołania procedury. Zawsze jest skierowana od przypadku wywołującego do przypadku wywoływanego. Wywołanie może być bezwarunkowe lub możemy dla niego określić warunek. Rysunek 2.10 pokazuje fragment zawierający wybranych aktorów i przypadki użycia z rysunku2.6, gdzie zamiast standardowych relacji zastosowano relacje «invoke».
Rysunek 2.10: Zastosowanie relacji «invoke»
Działanie relacji «invoke» ilustruje rysunek 2.11. Widzimy tutaj dwa warianty relacji. Pierwszy wariant jest opatrzony warunkiem. Spełnienie tego warunku po dotarciu do odpowiedniego punktu w przypadku użycia „Pokazanie listy samochodów” powoduje uruchomienie scenariuszy przypadku użycia „Usunięcie zamówienia samochodu”. W drugim wariancie, scenariusze przypadku użycia „Przejrzenie historii zamówienia” są bezwarunkowo uruchamiane po dotarciu do odpowiedniego punktu w scenariuszu przypadku użycia „Usunięcie zamówienia samochodu”.
Rysunek 2.11: Wywołanie przypadków użycia w relacji «invoke»
Podobnie jak dla aktorów, również między przypadkami użycia możliwe jest stosowanie relacji generalizacji. Oznacza ona, że przypadek użycia specjalizowany dziedziczy cechy przypadku użycia ogólnego. Rysunek 2.10 dostarcza nam odpowiedniego przykładu. Przypadek użycia „Wyszukanie zamówienia z możliwością anulowania” specjalizuje „Wyszukanie zamówienia”. Oznacza to, że sprzedawca ma do dyspozycji podstawową funkcjonalność wyszukiwania zamówienia, która m.in. umożliwia uruchomienie funkcjonalności „Pokazania historii zamówień klienta”. Kierownik ma natomiast do dyspozycji przypadek użycia, który rozszerza możliwość wyszukiwania zamówień o funkcjonalność przypadku użycia „Anulowanie zamówienia”.
2.5. Opisy historii użytkownika i przypadków użycia
Na poziomie wymagań użytkownika wystarczy znacznie bardziej uproszczony opis poszczególnych jednostek wymagań funkcjonalnych. Oczywiste jest jednak, że ograniczenie się do samych historii użytkownika w formie pojedynczych zdań lub diagramów zawierających przypadki użycia z relacjami nie jest wystarczające
Historie użytkownika są z zasady bardzo „lekkim” sposobem formułowania wymagań funkcjonalnych. Opis historii użytkownika nie jest zatem w szczególny sposób sformalizowany. Mimo to, przyjmuje się, że kompletna historia użytkownika powinna składać się z trzech zasadniczych elementów (tzw. KKK):
- Karta – jedno zdanie, mieszczące się na typowej karcie katalogowej, zapisane zgodnie z wzorem typu „Jako … chcę … aby …”. Sposób tworzenia karty został przedstawiony w sekcji 2.2.
- Konwersacja – dodatkowy opis historii użytkownika, który jest wynikiem dyskusji nad szczegółami wpływającymi na jej implementację. Konwersacja może być zapisana w swobodnym języku naturalnym.
- Konfirmacja (potwierdzenie) – zestaw kilku testów akceptacyjnych, które miałyby potwierdzić prawidłowość implementacji historii użytkownika. Testy na tym poziomie opisane są w sposób skrótowy i powinny się z zasady mieścić na drugiej stronie karty katalogowej.
Przykładowo, dla historii „Jako serwisant chcę zarejestrować brak części, aby móc dokończyć naprawę samochodu” moglibyśmy zanotować następującą „konwersację”:
- Serwisant powinien móc wybrać parametry części z dostępnej listy.
- Po wpisaniu częściowego numeru części system powinien podpowiedzieć cały symbol na podstawie katalogu części.
- Zgłoszenie braku części powinno być porównane ze stanem magazynu części.
Trzecim elementem opisu historii użytkownika, który możemy wykonać na poziomie wymagań użytkownika jest konfirmacja. Formułujemy tutaj proste zdania opisujące możliwe testy, które powinny udowodnić prawidłowość działania historii użytkownika. Przykładowo, testy dla przykładowej konwersacji mogłyby wyglądać następująco:
- Lista parametrów wyświetlona serwisantowi powinna zawierać wszystkie parametry wprowadzone podczas konfiguracji parametrów.
- Gdy serwisant wprowadzi kilka liter symbolu, powinien otrzymać podpowiedzi zgodne z aktualnym stanem katalogu części.
- Jeśli serwisant zgłosi brak części, a dana część jest w magazynie, powinno zostać pokazane ostrzeżenie.
Rysunek 2.12: Przykładowy opis przypadku użycia
Nieco inny sposób opisu stosowany jest typowo dla przypadków użycia, co ilustruje rysunek 2.12. Główna część zawiera podobną treść do konwersacji historii użytkownika. Opis jest wykonany w języku naturalnym, przy czym wyróżnione zostały pojęcia istotne dla danej dziedziny problemu („część”, „zgłoszenie braku części” itd.). W naszym przykładzie widzimy dodatkowo dwie sekcje zawierające warunki. Pierwszy warunek jest warunkiem rozpoczęcia (ang. precondition). Zawiera on stwierdzenia, które muszą być prawdziwe, aby przypadek użycia mógł być uruchomiony przez aktora głównego. Druga sekcja warunków zawiera warunki zakończenia (ang. postcondition). Może ich być kilka, gdyż wykonanie przypadku użycia może się zakończyć na różne sposoby (np. sukces lub porażka). Warunki zakończenia dają zatem rozeznanie w możliwej złożoności przypadku użycia z punktu widzenia możliwych alternatywnych przebiegów.