Projektowanie systemów informatycznych

Część 1

Sebastian Wyrwał

Artykuł stanowi wprowadzenie do zagadnień związanych z
projektowaniem systemów informatycznych. Przedstawia on pewne podstawowe pojęcia
związane z cyklem życia oprogramowania i jego wpływem na projektowanie. Określa
również wpływ analizy wymagań na ten proces. Dokonany został podział metod
projektowania pod względem stopnia sformalizowania i sposobu prezentacji różnych
aspektów systemu informatycznego. Opisuje także wytwarzanie oprogramowania
oparte o koncepcję rodzin produktów.

Aby przeprowadzić analizę metod projektowania, trzeba
zbudować kontekst, w którym czynność ta istnieje. Należy pamiętać, że
umiejętność projektowania jest wtórna do umiejętności programowania.

W początkowym okresie programy nie były projektowane – za
dobry uznawano program produkujący poprawne wyniki, abstrahując od możliwości
jego późniejszej rozbudowy. Projektowanie stało się potrzebne dla dużych
systemów, dla których niezawodność była czynnikiem kluczowym. Starannie
zaprojektowany system jest bardziej odporny na błędy i bardziej niezawodny.
Samo projektowanie ewoluowało równolegle do języków i metod programowania. Z
punktu widzenia teorii wszystko jest jasne: istnieją różne modele cyklu życia
oprogramowania, w których projektowanie ma ściśle określone miejsce. Rzecz
staje się jednak bardziej mglista w praktyce. Wiele osób niechętnie
projektuje lub nie robi tego wcale zdając się na improwizację. Często zdarza
się, że projekt co prawda istnieje, ale realizacja przebiega jakby osobnym
torem, mając z nim mało punktów stycznych. Czynnikiem kluczowym jest czas, często
przedkłada się bowiem szybkie uzyskanie rezultatu nad staranne projektowanie.
Można zapytać czym jest ów proces, jak również co projektujemy. Można, co
oczywiste, projektować sam produkt czyli system informatyczny, można również
projektować, co już nieco mniej oczywiste, proces produkcji tego systemu lub
pewną technologię. Można projektować równolegle produkt i proces jego
produkcji. Można wreszcie projektować bibliotekę wspomagającą programistów
określonych typów aplikacji, ramy aplikacji, protokoły, języki opisu
rzeczywistości lub języki programowania. Generalnie, projektować można
wszystko, co ma związek z systemem informatycznym. W niniejszym cyklu będziemy
jednak abstrahować od projektowania rozwiązań sprzętowych, skupiając się
na oprogramowaniu lub środkach służących do wytwarzania tego oprogramowania.
Można przyjąć, że projektowanie jest iteracyjnym procesem, który służy
zbudowaniu pewnego modelu lub opisu systemu informatycznego, ułatwiającego
wykonanie tego systemu. Projektowanie może być w najprostszych przypadkach
zupełnie nieformalne lub zupełnie sformalizowane w przypadkach, gdy od działania
systemu zależy życie ludzkie. Projektowanie można więc rozumieć jako
proces, który przekształca wymagania użytkownika w opis systemu. Stopień
sformalizowania procesu projektowania jest w zasadzie adekwatny do stopnia
sformalizowania samego projektu.

Projektowanie jako proces
Rys. 1. Projektowanie jako proces

Na rysunku [1] przedstawiono projektowanie jako proces, który
na podstawie wymagań stawianych systemowi tworzy pewien opis sposobu
konstrukcji systemu. Ten ostatni służy wytworzeniu systemu lub alternatywnie
wytworzeniu innego procesu służącego do wytworzenia systemu lub narzędzia
(grupy narzędzi) służącego do automatycznego generowania systemów w oparciu
o ich specyfikację. Prezentowany opis nie odzwierciedla cyklu życia
oprogramowania, służy natomiast umiejscowieniu projektowania.

W szeroko rozumianym projektowaniu systemów informatycznych
można wyróżnić następujące kategorie osób:

  • odbiorcy systemu,

  • analitycy,

  • eksperci,

  • projektanci,

  • programiści,

  • testerzy,

  • dokumentaliści.

Odbiorcy systemu to albo firma lub firmy zamawiające
duży system, albo klienci kupujący system sprzedawany „z półki”.

Analitycy to osoby odpowiedzialne za stworzenie
specyfikacji wymagań systemu. Specyfikacja wymagań zawiera zazwyczaj opis
rzeczywistości, słownik systemu i przypadki użycia systemu.

Eksperci z dziedziny w jakiej powstaje system, są
pomocni przy rozstrzyganiu sytuacji wątpliwych. Współpracują oni z
projektantami, programistami i testerami. Nie wszyscy zdają sobie z tego sprawę,
ale dobór odpowiednich, zaangażowanych we współpracę specjalistów
dziedzinowych może stanowić o sukcesie projektu programistycznego.

Projektanci wytwarzają mniej lub bardziej
sformalizowany opis systemu, który służy następnie programistom kodującym
system.

Testerzy dokonują testów systemu aby wykryć błędne
wykonania. Same błędy semantyczne powinny być wykrywane przez doświadczonych
programistów podczas analizy statycznej kodu. Warto wspomnieć, że siła tej
ostatniej jest często ignorowana. Jeszcze częściej pomija się ją w ogóle,
przechodząc od razu do testów. Trzeba zauważyć, że niektóre błędy
prowadzą do błędnych wykonań w bardzo specyficznych sytuacjach, są więc
bardzo trudne do wykrycia drogą samego testowania. Analiza statyczna, czyli uważne
czytanie i analizowanie kodu, najlepiej w postaci wydruku1 
jest bardzo mocnym narzędziem.

Dokumentaliści tworzą dokumentację techniczną
sytemu, jak również redagują systemy pomocy lub dokumentację dla użytkownika.
Trzeba pamiętać, że w dłuższej perspektywie skład zespołu realizującego
projekt lub/i wytwarzającego produkt zmienia się. Właściwie prowadzona, nie
banalna, dokumentacja ma zasadnicze znaczenie dla wdrożenia nowych członków
zespołu w realizowane zadanie. To samo dotyczy komentarzy w kodzie. Oczywiście
jeżeli dokumentaliści nie są informatykami, konieczna jest ich współpraca z
projektantami lub/i programistami.

Warto wspomnieć, że często jedna osoba jest odpowiedzialna
za dany podsystem na każdym etapie: od sformułowania wymagań, poprzez
projektowanie, programowanie aż po testowanie. Jest to jednak szczególny
przypadek, w którym analityk, projektant, programista i tester jest tą samą
osobą. Sytuacja taka sprzyja brakowi odpowiedniej dokumentacji technicznej, zwłaszcza
zawierającej szczegółowy opis przyjętego rozwiązania.

Zależnosci pomiędzy osobami w wytwarzaniu oprogramowania
Rys. 2. Zależności pomiędzy osobami w wytwarzaniu oprogramowania

Na rysunku [2] przedstawiono uproszczoną zależność pomiędzy
osobami uczestniczącymi w przedsięwzięciu informatycznym. Dokładne współdziałanie
osób i procesów realizujących przedsięwzięcie zależy od przyjętego modelu
cyklu życia oprogramowania. Z punktu widzenia tego artykułu najistotniejsi są
projektanci i programiści, oraz styk ich działalności. Artefaktami
(produktami ubocznymi) obu procesów jest dokumentacja. Sam proces projektowania
może być podzielony na:

  • modelowanie,

  • właściwe projektowanie.

Modelując system abstrahujemy od wszelkiego rodzaju rozwiązań
technicznych skupiając się na jego właściwościach, a nie realizacji. Używając
metod obiektowych na poziomie modelowania, uwzględniamy wyłącznie klasy-encje
występujące w opisie rzeczywistości.

Na zakończenie tego wstępu warto przypomnieć, że metody
projektowania mogą być zupełnie nieformalne, wręcz humanistyczne, półformalne
lub formalne, których zwięzłość odpowiada zwięzłości zapisu stosowanego
w logice matematycznej lub matematyce. Zagadnienie poruszane w tym cyklu będą
skupiać się na właściwym projektowaniu oraz jego „styku” z
programowaniem (pośrednio testowaniem) i analizą – zwłaszcza związaną z
modelowaniem. Poza tym, poruszanymi zagadnieniami będą wymagania i rozwiązania
sprzętowe.

Cykle życia oprogramowania a projektowanie

Cykl życia oprogramowania opisuje historię systemu
informatycznego począwszy od jego narodzin aż do wdrożenia i utrzymania. Można
przyjąć, że opisuje on zależności (czasowe) pomiędzy różnymi aktywnościami
podejmowanymi przy szeroko rozumianym wytwarzaniu oprogramowania. Z punktu
widzenia projektowania określa on, jak proces projektowania współdziała z
innymi procesami. Cykl życia wpływa także na wymagania stawiane procesowi
analizy i projektowania oraz samemu projektowi.

Popularnym, w teorii inżynierii oprogramowania cyklem życia
oprogramowania jest model kaskadowy (ang. waterfall), w którym kolejne aktywności
następują po sobie bez możliwości powrotu, co przedstawia rysunek [3].

Model kaskadowy
Rys. 3. Model kaskadowy

  • Rezultatem analizy wymagań jest, w klasycznym przypadku,
    dokument określający, co system ma robić z punktu widzenia użytkownika –
    czyli wymagania funkcjonalne. Abstrahuje się od tego, jak system ma je
    realizować. W praktyce określa się jeszcze wymagania niefunkcjonalne (np.
    czas odpowiedzi na żądanie użytkownika, wydajność) abstrahując, w
    dalszym ciągu, od sposobów realizacji tych wymagań. Do tego etapu można
    zaliczyć również stworzenie modelu systemu lub/i jego prototypu. W modelu
    kaskadowym istotne jest to, że po wyjściu z fazy analizy, wymagania
    stawiane systemowi nie mogą być już zmienione.

  • Na etapie projektowania określa się, mówiąc ogólnie,
    jak system ma realizować wymagania określone na etapie analizy wymagań.
    Projektowanie może oczywiście odbywać się na różnych poziomach
    abstrakcji – od ogólnego do bardzo szczegółowego, uwzględniającego
    nazwy zmiennych w kodzie. W praktyce stosuje się projekty koncepcyjne określające
    istotę rozwiązań. W systemie bankowym mogą to być encje wynikające z
    jego opisu rzeczywistości np. klient, konto, ale nie słownik imion
    zastosowany w rejestrze osób dla zmniejszenia wielkości bazy danych.
    Projekt koncepcyjny jest przydatny zwłaszcza w sytuacji kiedy zrezygnowano
    z modelowania w poprzednim etapie. Granica pomiędzy modelem a projektem
    koncepcyjnym jest bardzo płynna. Stopień uszczegółowienia projektu zależy
    również od swobody, jaka ma być pozostawiona programistom. Ze względu na
    to, że model kaskadowy nie pozwala na nawroty, nie poleca się zbytniego
    uszczegóławiania projektu na tym etapie.

  • Etap oprogramowania polega na zakodowaniu rozwiązań w języku
    programowania. Jeśli projekt nie jest zbyt szczegółowy, programiści
    uszczegóławiają go, co odpowiada realizacji szczegółowego projektu.
    Uszczegółowiony projekt powinien jednak być udokumentowany. Pozostawienie
    go jedynie w umysłach osób go realizujących jest częstym i karygodnym błędem.
    Na etapie oprogramowania może być dokonana integracja podsystemów lub
    modułów wchodzących w skład systemu. Etap ten obejmuje również wstępne
    testowanie na poziomie funkcji lub modułów. Programiści poprawiają zauważone
    przez siebie błędy.

  • Na etapie testowania dokonywana jest próbna eksploatacja
    systemu przez testerów znających dziedzinę, w której działa system.
    Rezultatem tego etapu ma być przekształcenie wersji beta systemu w wersję
    dla użytkownika.

  • Wdrożenie i utrzymanie obejmuje instalację i
    konfigurację systemu u klienta oraz dostarczenie wparcia technicznego. W
    produktach „z półki” wdrożeniu odpowiadają działania
    marketingowe zmierzające do wprowadzenia produktu na rynek.

Autor nie spotkał się z sytuacją, w której model
kaskadowy byłby stosowany w praktyce; niemniej stanowi on wygodny punkt
odniesienia dla dalszych rozważań. Warto zauważyć, że zastosowany w
praktyce byłby bardzo wygodny dla projektantów i programistów, gwarantuje on
bowiem niezmienność raz określonych wymagań. Odbiorcy prezentuje się
dopiero gotowy produkt (prototyp prezentuje raczej interfejs użytkownika bez
rzeczywistej funkcjonalności, może tę funkcjonalność także symulować).

Pewną modyfikacją modelu kaskadowego jest wprowadzenie do
niego nawrotów (model kaskadowy z nawrotami). Możliwość powrotu do
poprzedniego etapu powoduje, że model może być zastosowany w praktyce.
Skutkuje to również tym, że projektanci i programiści tracą gwarancję
niezmienności wymagań.

Jeszcze inną modyfikacją modelu kaskadowego jest model
przyrostowy, w którym funkcjonalność systemu dzielona jest na podzbiory. Każdy
z nich, poczynając od podstawowej funkcjonalności, jest realizowany osobno
zgodnie z modelem kaskadowym lub kaskadowym z nawrotami. W praktyce jednak
realizacja poszczególnych funkcjonalności zazębia się, co zdaniem autora
prowadzi do improwizacji lub mówiąc kolokwialnie, do łatania systemu.
Rozbudowa systemu może być prowadzona jednak inaczej: dla całego systemu
realizującego pewną podstawową funkcjonalność wykonywany jest cały cykl
związany z modelem kaskadowym (lub kaskadowym z nawrotami) po czym system
dostarczany jest odbiorcy. Ten ostatni testuje go i rozszerza specyfikację o
nowe wymagania, które są realizowane w pełnym cyklu modelu kaskadowego.

Model oparty o prototypowanie zakłada zebranie wymagań
dotyczących sytemu, realizację projektu i implementacji, a następnie
zaprezentowanie wyników użytkownikowi i powrót do punktu określania wymagań.
Jest to o tyle różne od modelu przyrostowego, że uwaga osób realizujących
projekt jest bardziej skupiona na realizacji prototypu niż na formalnym
prowadzeniu projektu. Model ten przedstawiono na rysunku [4]:

Prototypowanie
Rys. 4. Prototypowanie

Model ten może znajdować zastosowanie dla niezbyt dużych i
mało sformalizowanych projektów systemów, od których nie zależy życie lub
bezpieczeństwo ludzi lub środków finansowych w banku. Dobrym przykładem może
być sklep internetowy, program graficzny lub edukacyjny. Nie wymaga on dużej
wiedzy i umiejętności od projektantów i analityków. Jest odporny na błędy
na etapie analizy i projektowania. Jest on również przydatny wtedy, gdy
wymagania klienta są niejasno sformułowane.

Model spiralny zakłada cykliczne przechodzenie przez kolejne
fazy realizacji projektu, istnieje on jednak w nieco innym wymiarze – nie wyróżnia
bowiem aktywności takich jak projektowanie, programowanie itp. ale cztery
cyklicznie powtarzane etapy:

  1. Określanie wymagań i planowanie,

  2. Analiza ryzyka,

  3. Wytworzenie systemu,

  4. Ocena użytkownika i przejście do punktu 1.

Każde przejście spirali odpowiada wytworzeniu kompletnego
produktu. Analiza ryzyka prowadzi do decyzji, czy projekt ma być kontynuowany.
Warto zauważyć, że model ten jest podobny do prototypowania. W modelu tym
nawroty występują dopiero w kolejnej iteracji spirali.

Model spiralny
Rys. 5. Model spiralny

Jako kontrapunkt dla prezentowanych dotąd modeli, zakładających
(mniej lub bardziej staranne i sformalizowane) analizę i projektowanie, należy
przedstawić model programowania odkrywczego. W modelu tym nie ma określenia
wymagań, nie ma też projektowania. Buduje się produkt i modyfikuje, aż
uzyska pożądane właściwości. Niestety model ten jest nazbyt często
stosowany. Jego użycie jest bardzo frustrujące dla programistów.

Innym podejściem jest budowanie systemu z gotowych,
przetestowanych komponentów, co nie jest typowym budowaniem systemu na poziomie
programowania, niemniej jednak jest jedną z metod służących do wytworzenia
systemu. Wymaga analizy a następnie znalezienia (lub zamówienia) komponentów
realizujących określone funkcje. W podejściu tym nie występuje
projektowanie.

Nowością jest wytwarzanie systemów, oparte o koncepcje
rodzin. Polega ono na starannej analizie i projektowaniu narzędzi służących
do wytwarzania systemów informatycznych realizujących podobne funkcje – np.
systemów bankowych. Wraz z narzędziami tworzy się język opisu wymagań dla
systemu, który ma być wytworzony. Nie jest to jednak projekt, gdyż opisuje się,
co system ma robić abstrahując od sposobu realizacji wymagań. Konkretny
system generuje się automatycznie, na podstawie opisu wymagań w tym języku.

W tabeli [1] porównano różne modele cykli życia
oprogramowania oraz przedstawiono ich wpływ na analizę, projektowane,
programowanie i testowanie. Z tabeli widać, że im mniej dokładna analiza i
projektowanie tym większy ciężar przerzucony jest na programistów. W
praktyce, ofiarą niestarannego projektowania, i braku przenikliwości samych
projektantów i analityków (to samo odnosi się do ekspertów dziedzinowych)
padają programiści „bo system i tak musi działać”. Staranne
projektowanie ułatwia pracę programistom i testerom oraz pozwala zaoszczędzić
nakłady w procesie programowania i wdrożenia. Im większym zmianom ma podlegać
system tym przyjęte rozwiązania muszą być bardziej elastyczne i ogólne. Można
jeszcze wspomnieć, że zasadniczym zadaniem ekspertów dziedzinowych ma być
wskazanie obszarów systemu, które mogą podlegać potencjalnym zmianom.

Tabela 1. Zestawienie modeli cykli życia
oprogramowania

model cyklu życia oprogramowania  analiza    projektowanie programowanie testowanie
Kaskadowy  Bardzo dokładana  Staranne, ale nie narażone na ciągłe zmiany. Znaczna część pracy przerzucona na projektantów i programistów.  Łatwe jeśli dobry projekt.  Testy na poziomie funkcji modułów i integracyjne.
Kaskadowy z nawrotami  Dokładana  Staranne, ale narażone na pewne zmiany Znaczna część pracy przerzucona na projektantów i programistów.  Dość łatwe ale musi uwzględniać możliwość pewnych zmian.  Nawroty mogą spowodować, że modyfikacje w jednym miejscu systemu spowodują jego nie działanie w innym
Przyrostowy  Dokładna w obrębie podzbioru.  Łatwe na początku bo projektuje się mały podzbiór, jednocześnie przy rozszerzaniu funkcjonalności jest się związanym tym co się wcześniej zaprojektowało. System należy tak projektować aby był konfigurowalny.  Prowadzi od improwizacji, wymaga dużej wiedzy od programistów. Po wprowadzeniu nowej funkcjonalności trzeba sprawdzić czy poprawnie funkcjonuje dotychczasowa
Prototypowa    Nie wymaga dużej przenikliwości.  J.w. Znaczna część pracy przerzucona na programistów.  Część testowania przerzucona na odbiorcę systemu.
Odkrywczy  Brak  Brak  Czasem trzeba wszystko zrobić od nowa. Kod nieprofesjonalny.  Bardzo dokładne.
Komponentowy  Dokładna  Brak  Znikome lub brak  Testy integracyjne.

Oprogramowanie oparte o koncepcji rodzin 

  • Dokładna zarówno dla rodziny i narzędzi z nią związanych.
  • Bardzo dokładna dla produktu. Specyfikacja produktu w języku specjalnie zaprojektowanym dla całej rodziny.
  • Bardzo dokładne dla narzędzi dla całej rodziny i języka opisu członków rodziny.
  • Dla konkretnego produktu – brak.
  • Bardzo dokładne dla narzędzi dla całej rodziny.
  • Wykonywane automatycznie dla konkretnych produktów.
  • Dokładne dla narzędzi i języka opisu
  • Szczątkowe (tylko cały system) dla produktu. Jeśli dobrze
    skonstruowane narzędzia i specyfikacja zbędne

Analiza wymagań użytkownika

Analiza wymagań określonych przez użytkownika dla systemu
dedykowanego, lub przez ekspertów dla systemu „z półki” jest
pierwotna w stosunku do jakiegokolwiek projektu, co uwzględnia większość
cykli życia oprogramowania. Projekt nie poprzedzony analizą wymagań, jest
chybiony. Czas i inne nakłady zaoszczędzone w tym miejscu, zostaną poniesione
z nawiązką przy projektowaniu, a w jeszcze większym stopniu przy
programowaniu. W pracy [7] podano, że wzrost kosztów związanych z błędną
analizą wzrasta dziesięciokrotnie w każdej następnej fazie projektu (model
kaskadowy). Dla niebanalnych systemów analiza wymagań powinna być
przeprowadzona iteracyjnie i zakończona walidacją lub/i budową modelu systemu
lub szkieletu systemu, który zostanie zaprezentowany użytkownikowi. W praktyce
sprawy się komplikują, ponieważ wymagania użytkownika są niejasne, niespójne
i sprzeczne. Zmieniają się też w czasie. Użytkownik często nie wie tak
naprawdę, czego oczekuje. W dodatku systemy związane z działalnością
gospodarczą są w znacznym stopniu zanurzone w istniejących uregulowaniach
prawnych, które się zmieniają. Należy zwrócić więc baczną uwagę, aby
nie usztywnić w zbyt dużym stopniu tych wymagań, które mogą być
przedmiotem późniejszych zmian. W literaturze [3,9] opisano metodę
wytwarzania rodzin produktów posiadających pewne wspólne cechy. Dla całej
rodziny jest tworzony pewien specyficzny język opisu, charakterystyczny dla
dziedziny, w obrębie której mają działać systemy. Równolegle tworzone są
narzędzia pozwalające wygenerować konkretny system na podstawie jego opisu.
Takie podejście można rozumieć jako analizę wymagań i projektowanie bardzo
elastycznego systemu, który następnie jest konfigurowany. Podejście to jest
jednak skrajnie inne od podejścia, które zakłada wytworzenie sytemu dla
konkretnego odbiorcy, a następnie przystosowanie tego systemu i stworzenie
nowej wersji dla innego odbiorcy. Można powiedzieć, że w podejściu opisanym
w pracy [3,9] samą analizę konkretnych wymagań użytkownika zastępuje się
konstrukcją języka opisu potencjalnych (sensownych) wymagań dowolnego użytkownika.

Wymagania stawiane systemowi można podzielić na
funkcjonalne i niefunkcjonalne. Wymagania funkcjonalne określają co system
robi, wymagania niefunkcjonalne określają, jak system realizuje wymania
funkcjonalne. Ważną częścią analizy wymagań jest określenie wymagań
stawianych interfejsowi użytkownika systemu informatycznego. Interfejs użytkownika
wpływa nie tylko na ergonomię systemu ale również na komercyjny sukces lub
porażkę sytemu. Należy pamiętać, że potencjalny użytkownik postrzega
system właśnie przez interfejs użytkownika, nie mając pojęcia o algorytmach
czy rozwiązaniach realizujących funkcjonalność systemu. Informatycy są
najczęściej zainteresowani rozwiązaniami typowo technicznymi lub
algorytmicznymi, lekceważąc często estetykę i ergonomię systemu. Pracownicy
firmy stosującej system informatyczny, są przyzwyczajeni do interfejsu i
sposobu użycia tego systemu. Może okazać się, że nowy system nie jest
dobrze przyjęty tylko dlatego, że jego obsługa różni się od tej, do której
przywykli pracownicy. Można powiedzieć, że dla personelu bez wykształcenia
informatycznego, preferuje się graficzny interfejs użytkownika

Należy zaznaczyć, że skutkiem analizy jest zrozumienie
informatyzowanej rzeczywistości, w obrębie której ma działać system. Trzeba
jeszcze raz podkreślić, że zrozumienie tej rzeczywistości ma kluczowy
charakter zarówno dla całego dalszego procesu projektowania jak i dla
komercyjnego powodzenia całego przedsięwzięcia. Użytkownicy nie zawsze zdają
sobie sprawę z tego, co jest ważne a co nie dla twórców systemu, tak więc
analiza wymagań jest niczym innym jak definiowaniem sytemu. Rozwój systemu, którego
analiza została poprawnie przeprowadzona przebiega gładko i w logiczny sposób.
Niedociągnięcia skutkują sprzecznymi i niespójnymi założeniami. Podczas
analizy wymagań powstaje dokument opisujący wymagania funkcjonalne i
niefunkcjonalne systemu. Opcjonalnie może też powstać model systemu
informatycznego. Analiza powinna określać ponadto:

  • wymagania sprzętowe systemu, wymagany system operacyjny
    i inne oprogramowanie,

  • wymagania dotyczące przenośności,

  • interfejs użytkownika,

  • dokumentację i pliki pomocy towarzyszące systemowi,

  • systemy z którymi projektowany system ma być
    kompatybilny,

  • format plików z danymi lub format danych (np. istniejący
    model bazy danych),

  • wymagania dotyczące bezpieczeństwa,

  • wymagania dotyczące czasu odpowiedzi na żądanie użytkownika,

  • modyfikacje i zmiany jakim może podlegać system,

  • wymagania dotyczące niezawodności,

  • wymagania dotyczące samego projektu.

Można nałożyć także ograniczenia na język lub języki
implementacji produktu, jakkolwiek – zdaniem autora – decyzje technologiczne
powinny zapadać po stronie wytwórcy oprogramowania. Poszczególne wymagania
powinny podlegać weryfikacji i walidacji. W literaturze [4] podano następujące
metody weryfikacji wymagań:

  • Sprawdzenie poprawności wymagań przez wytwórcę
    oprogramowania i klienta (eksperta gdy system jest „z półki”),

  • Sprawdzenie spójności wymagań. Wymagania nie mogą być
    sprzeczne,

  • Sprawdzenie pełności wymagań tzn. czy opisują
    wszystkie możliwe sytuacje, jakie mogą zajść i muszą być
    „odwzorowane” w systemie informatycznym,

  • Sprawdzenie, czy wymagania są realne tzn. czy mogą być
    spełnione przy obecnym stanie techniki,

  • Sprawdzenie, czy wszystkie wymagania są niezbędne z
    punktu widzenia użytkownika sytemu,

  • Sprawdzanie, czy wymagania są weryfikowalne, tzn. czy można
    opracować procedury testujące dla poszczególnych wymagań. Innymi słowy
    – wymagania muszą być tak zdefiniowane, aby można było sprawdzić czy są
    spełnione.

Przy niebanalnych systemach, najwięcej problemów sprawia
weryfikacja wymagań z użytkowaniem, i określenie czy wymagania
„pokrywają” złożoną informatyzowaną rzeczywistość. Z doświadczeń
autora wynika, że duże problemy mogą występować przy kontaktach z
ekspertami dziedzinowymi. Jak już powiedziano ich opinie bywają sprzeczne, a
czasami określane przez nich wymagania ciągle się zmieniają. Jest to często
dotkliwy problem przy prezentacji wyników i wszelkiego rodzaju wydrukach, mniej
zaś dotyczy samej realizacji systemu, gdyż ta jest dla eksperta niewidoczna.
Teoria mówi, że analiza i projektowanie powinny być całkowicie niezależne
od implementacji. W praktyce jednak firma wytwarzająca oprogramowanie narzuca
technologię i rozwiązania techniczne jakie należy stosować. Problem polega
na tym, że eksperci dziedzinowi nie zawsze zdają sobie sprawę z ograniczeń
technologicznych. Trzeba również zdać sobie sprawę, iż duży system
tworzony jest przez wiele osób przez kilka lat, a projektowany właśnie
podsystem ma wykorzystywać istniejące już rozwiązania i jest nimi
ograniczony.

Należy pamiętać, że formalna specyfikacja, jednoznaczna
dla informatyków, jest zazwyczaj zupełnie niezrozumiała dla użytkowników.
Jedynie zbudowanie modelu systemu i jego prezentacja daje względną gwarancję
tego, że wymagania w stosunku do systemu są jednakowo rozumiane przez użytkowników,
analityków systemowych i projektantów.

W tabeli [2] zestawiono różne czynności i metody związane
z analizą wymagań, trzeba jednak zdać sobie sprawę, że sposób opisu wymagań
stawianych systemowi, choć istotny, jest zawsze wtórny w stosunku do zawartości
merytorycznej. Ważne jest także to, aby wymagania się nie zmieniały podczas
trwania projektu. Na etapie analizy wymagań należy antycypować możliwe
zmiany. Przy pomocy dobrze skonstruowanego opisu słownego można bez trudu
stworzyć diagram przypadków użycia. Z punktu widzenia projektowania ważna
jest jakość (w sensie wymienionych wcześniej cech) i czytelność opisu
wymagań. Autor preferuje słowny opis rzeczywistości wraz ze słownikiem
systemu i diagramami przypadków użycia, sporządzony na podstawie wywiadów ze
specjalistami dziedzinowymi.

Inżynieria rodzin produktów

Wytwarzanie rodzin produktów, o którym już wspomniano przy
opisie cykli życia oprogramowania, jest podejściem nowym. Podejście to jest
związane w mniejszym stopniu z projektowaniem, czy analizą wymagań w dosłownym
tego słowa znaczeniu. Jest to nowy paradygmat wytwarzania oprogramowania. Swój
początek bierze on z gwałtownych zmian potrzeb rynku nastawionego na coraz to
nowe produkty z jednej strony, a wysoką jakość oprogramowania z drugiej. Jak
podano w pracy [3, 9], staranne projektowanie jest wymaganiem stojącym w
opozycji do szybkiego wytworzenia systemu informatycznego. W podejściu tym nie
wytwarza się pojedynczego systemu, ale całą ich rodzinę, a raczej narzędzia
do tworzenia członków tej rodziny. W obrębie wytwarzania narzędzi i języka
definicji dla całej rodziny można więc wyróżnić:

  • inżynierów dziedzinowych,

  • analityków dziedzinowych,

  • projektantów i programistów dziedzinowych.

Za wytwarzanie konkretnej aplikacji (która jest członkiem
rodziny) odpowiada inżynier aplikacyjny, który może mieć dużo mniejsze
kwalifikacje niż specjaliści dziedzinowi.

Kluczowe znaczenie w opisywanym podejściu ma proces FAST, który
został schematycznie przedstawiony (za [3]) na rysunku [6].

Proces FAST
Rys. 6. Proces FAST

Określenie dziedziny polega na znalezieniu [3, 9] zbioru
programów o podobnej funkcjonalności. Dziedzina jest więc zarówno zbiorem
programów, jak i fragmentem rzeczywistości, w której te programy istnieją.
Kluczowe jest znalezienie abstrakcji dla tej rodziny produktów, zdefiniowanie języka
służącego do opisu członków rodziny i zbudowanie narzędzi służących do
generowania aplikacji na podstawie opisu w tym języku. U podstaw metody leży
założenie, że wytwarzanie aplikacji polega w większości przypadków na
wytwarzaniu rzeczy już istniejących. W artykule [3] wymieniono typowe
zastosowania procesu FAST:

  • Systemy mające te same wymagania, ale mające działać
    na różnych platformach (motory bazy danych, kompilatory),

  • Systemy przetwarzające w różny sposób te same dane,

  • Systemy monitorujące urządzenia tego samego typu, ale
    zachowujące się w odmienny sposób,

  • Systemy z takim samym interfejsem użytkownika, ale
    implementujące swoją funkcjonalność w odmienny sposób.

Nakłady poniesione na inżynierię aplikacyjną zwracają się
dzięki możliwości natychmiastowego tworzenia produktów. Zastosowanie procesu
FAST może przyspieszyć wytworzenie aplikacji od 5 do 10 razy w stosunku do
wytworzenia jej z użyciem tradycyjnych metod. Trzeba zdać sobie sprawę, że
kluczem do sukcesu, w tym podejściu, jest poprawne wydzielenie dziedziny (np.
systemy bankowe, systemy wspomagające analizę danych naukowych) oraz bardzo
starannie przeprowadzona analiza wymagań systemu; innymi słowy język opisu
musi być na tyle elastyczny, aby można w nim było opisać np. dowolny system
bankowy, jeżeli dziedziną jest dziedzina takich systemów. Język musi być
jednoznaczny. Odnosząc się do języków programowania, język takiego opisu mógłby
być bardziej zbliżony do języków nieimperatywnych, w których opisujemy co
ma być zrobione, niż do tradycyjnych języków imperatywnych opisujących
proces opisania problemu. Oczywiście jest to bardziej analogia, której nie
należy rozumieć tak, że systemy specyfikuje się w języku Prolog. Istotne
jest to, że język opisu jest specyficzny dla dziedziny a nie ogólny. Języki
specyfikacji systemu, w których można dokonywać specyfikacji implicite (tzn.
nie opisując sposobu rozwiązania ale jego postać) są znane: przykładem może
być VDM i jego odmiany.2  Specyfikację
wyrażoną w tym języku przekształca się przy użyciu narzędzi w system
informatyczny. Postępowanie opisane w pracy [9] bazuje na pewnego rodzaju
szkieletach funkcji lub kodu w ogóle, które są modyfikowane i
parametryzowane. W wyniku tego uzyskujemy kod, który można skompilować
standardowym kompilatorem. Można w tym podejściu rozważać także użycie
komponentów do budowy systemów. Można wyobrazić sobie np. komponent służący
do wprowadzania danych na podstawie pewnej specyfikacji np. w języku podobnym
do XML.

Podział metod projektowania

Metody projektowania można podzielić pod względem ich
sformalizowania, czyli zwięzłości i ścisłości opisu oraz sposobu
przedstawienia różnych aspektów systemu. Stosując pierwsze kryterium można
wyodrębnić metody:

  • nieformalne,

  • półformalne,

  • formalne.

Stosując drugie kryterium można wyodrębnić metody:

  • strukturalne,

  • obiektowe,

  • hybrydowe.

Metody nieformalne bazują na słownym opisie
rzeczywistości w języku naturalnym [6], który jest elastyczny, ale bardzo
niejednoznaczny. Zaletą opisu słownego jest to, że można go łatwo
zaprezentować klientowi. Różnego rodzaju notatki mogą mieć zastosowanie w
przypadku tworzenia oprogramowania przez jedną osobę. Oprogramowanie stworzone
w ten sposób jest jednak całkowicie nieodporne na modyfikacje. Stosując takie
podejście można wytworzyć sterownik napisany w asemblerze, ale nie system
informatyczny.

Metody półformalne opierają się o pewien
graficzny lub symboliczny zapis. Do tej grupy należy większość powszechnie
stosowanych metod np. UML.

Metody formalne są stosowane rzadko i przeważnie w
systemach, w których zachodzi konieczność przeprowadzenia matematycznego
dowodu poprawności działania systemu. Projektowanie i wytwarzanie systemu
odbywa się drogą formalnych przekształceń. Trudno sobie jednak wyobrazić
projektowanie systemu bankowego z użyciem metod formalnych. Inżynieria oparta
o rodziny produktów nie jest ściśle metodą formalną, uznaną przez teoretyków,
choć ideowo odpowiada metodom formalnym. Istnieje (symboliczny) opis systemu
podlegający (automatycznym) przekształceniom w system. Wspomniany VDM znajduje
się, zdaniem autora, na styku metod formalnych i nieformalnych. W tym języku
zaprojektowano np. system nadzoru ruchu kolejowego w Wielkiej Brytanii.

Metody strukturalne zakładają dekompozycję
informatyzowanej rzeczywistości (czyli przyszłego systemu) na:

  • dane,

  • procesy operujące na tych danych.

System informatyczny składa się z modułów, między którymi
mogą zachodzić różne związki. W obrębie jednego modułu mogą znajdować
się procedury działające na wspólnych danych lub realizujące podobną
funkcjonalność np. interakcję z użytkownikiem. Dekompozycja systemu na moduły
jest kluczowa dla powodzenia dalszego projektu. Systemy zaprojektowane z użyciem
metod strukturalnych są trudno modyfikowalne i trudne w integracji [6], zwłaszcza
przy złym podziale na moduły, co dla dużych systemów nie jest banalne.
Metody strukturalne są wystarczające dla prostych systemów dostępu do
danych.

Metody obiektowe umożliwiają spójne przedstawienie
systemu poprzez stworzenie typów danych, które są tak samo wygodne w użyciu,
jak typy danych wbudowane w język programowania. Dla tych metod istnieje wiele
narzędzi typu case wspomagających analizę i projektowanie.
Zastosowanie metod obiektowych wymaga jednak większego wysiłku niż metod
strukturalnych [10]. System informatyczny składa się z klas (Java, C++) lub
pakietów (Ada 95), które umożliwiają programowanie (i projektowanie
obiektowe). Siłą metod obiektowych jest możliwość ponownego użycia kodu
poprzez dziedziczenie i polimorfizm. Klasy pozwalają na wygodną i elegancką
hermetyzację bytów programowych.

Metody hybrydowe stanowią połączenie metod
obiektowych ze strukturalnymi. Trzeba zdać sobie sprawę, że duży system
informatyczny jest rzadko projektowany i implementowany obiektowo. Projektowanie
obiektowe jest, przy pewnej wprawie, bardzo wygodne. Problemy pojawiają się
jednak na poziomie bazy danych. W powszechnym użyciu są relacyjne bazy danych,
zaś języki takie jak PL SQL (Oracle) lub SPL (Informix)
są językami proceduralnymi: w bazie znajdują się dane na których operujemy
(procedury). W relacyjnej bazie danych nie można wprost zamodelować
dziedziczenia lub polimorfizmu, które są typowe dla metod obiektowych. Tak więc
na poziomie „bliskim” bazie danych projekt staje się bardziej
strukturalny. Jest to bardzo częste rozwiązanie. Możliwa jest następująca
struktura systemu informatycznego:

  • Dane w bazie danych (tabele),

  • Procedury wbudowane, dostęp do danych wyłącznie z ich
    użyciem,

  • Warstwa biznesowa realizująca funkcjonalność systemu
    (C++),

  • Warstwa prezentacji – interfejs użytkownika.

Metody występujące w każdej z powyższych kategorii zostaną
omówione bardziej szczegółowo w kolejnych odcinkach, z uwzględnieniem ich
wymagań, wad i zalet. Podsumowując zaprezentowany podział, autor pragnie
podkreślić, że nie warto dogmatycznie trzymać się jednej tylko metody.
Metoda powinna być tak użyta aby umożliwiała łatwe osiągnięcie rezultatu.
Pragmatyzm nie może jednak dotyczyć zastosowania konkretnej metodologii. Autor
spotkał się z sytuacją, gdzie przypadki użycia UML reprezentujące zawsze
skończoną funkcjonalność, stosowane były tak, jak aktywności opisujące
kolejne kroki w obrębie danej aktywności. Takie podejście jest ewidentnym
przykładem niezrozumienia metodologii. Trzeba pamiętać, przede wszystkim o
tym, aby każdą metodę stosować poprawnie.

c.d.n.

Literatura

[1] www.uidesign.net/1999

[2] L.A. Maciaszek Requirements Analysis and System Design.
Developing Information Systems with UML
, Addison Wesley

[3] David M. Weiss Software Sysnthesis: The FAST Process AT&T
Bell Laboratories, Software Production Research

[4] Pfleeger, Shari Lawrence Software Engineering: The
production of Quality Software
McMillan, New York, 1991

[5] T. Szmuc Modele i metody inżynierii oprogramowania
systemów czasu rzeczywistego
Kraków 2001

[6] A. Jaszkiewicz Inżynieria oprogramowania Helion
1997

[7] Pod redakcją J. Górskiego Inżynieria oprogramowania
w projekcie informatycznym
Mikom 2000

[8] Booch Grady, Rumbaugh James, Jacobson Ivar UML
przewodnik użytkownika
WNT 2001

[9] David M. Weiss and Chi Tau Robert Lai Software Product-Line
Engineering: A Family-Based Software Development Process
Addison-Wesley 1999

[10] B. Stroustrup Język C++ WNT 1995


Przypisy:
1 Chodzi o istotne fragmenty.
2 VDM jest metodą, ale do opisu systemu używa się
języka. Będzie on opisany w dalszym ciągu tego cyklu.